aboutsummaryrefslogtreecommitdiffstats
path: root/lib/kernel/test/erl_prim_loader_SUITE.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/kernel/test/erl_prim_loader_SUITE.erl')
-rw-r--r--lib/kernel/test/erl_prim_loader_SUITE.erl517
1 files changed, 517 insertions, 0 deletions
diff --git a/lib/kernel/test/erl_prim_loader_SUITE.erl b/lib/kernel/test/erl_prim_loader_SUITE.erl
new file mode 100644
index 0000000000..4d090f4db5
--- /dev/null
+++ b/lib/kernel/test/erl_prim_loader_SUITE.erl
@@ -0,0 +1,517 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(erl_prim_loader_SUITE).
+
+-include_lib("kernel/include/file.hrl").
+-include("test_server.hrl").
+
+-export([all/1]).
+
+-export([get_path/1, set_path/1, get_file/1,
+ inet_existing/1, inet_coming_up/1, inet_disconnects/1,
+ multiple_slaves/1, file_requests/1,
+ local_archive/1, remote_archive/1,
+ primary_archive/1]).
+
+-export([init_per_testcase/2, fin_per_testcase/2]).
+
+%%-----------------------------------------------------------------
+%% Test suite for erl_prim_loader. (Most code is run during system start/stop.)
+%%-----------------------------------------------------------------
+
+all(suite) ->
+ [
+ get_path, set_path, get_file,
+ inet_existing, inet_coming_up,
+ inet_disconnects, multiple_slaves,
+ file_requests, local_archive,
+ remote_archive, primary_archive
+ ].
+
+init_per_testcase(Func, Config) when atom(Func), list(Config) ->
+ Dog=?t:timetrap(?t:minutes(3)),
+ [{watchdog, Dog}|Config].
+
+fin_per_testcase(_Func, Config) ->
+ Dog=?config(watchdog, Config),
+ ?t:timetrap_cancel(Dog).
+
+get_path(doc) -> [];
+get_path(Config) when is_list(Config) ->
+ ?line case erl_prim_loader:get_path() of
+ {ok, Path} when is_list(Path) ->
+ ok;
+ _ ->
+ test_server:fail(get_path)
+ end,
+ ok.
+
+set_path(doc) -> [];
+set_path(Config) when is_list(Config) ->
+ ?line {ok, Path} = erl_prim_loader:get_path(),
+ ?line ok = erl_prim_loader:set_path(Path),
+ ?line {ok, Path} = erl_prim_loader:get_path(),
+ NewPath = Path ++ ["dummy_dir","/dummy_dir/dummy_dir"],
+ ?line ok = erl_prim_loader:set_path(NewPath),
+ ?line {ok, NewPath} = erl_prim_loader:get_path(),
+
+ ?line ok = erl_prim_loader:set_path(Path), % Reset path.
+ ?line {ok, Path} = erl_prim_loader:get_path(),
+
+ ?line {'EXIT',_} = (catch erl_prim_loader:set_path(not_a_list)),
+ ?line {ok, Path} = erl_prim_loader:get_path(),
+ ok.
+
+get_file(doc) -> [];
+get_file(Config) when is_list(Config) ->
+ ?line case erl_prim_loader:get_file("lists" ++ code:objfile_extension()) of
+ {ok,Bin,File} when binary(Bin), list(File) ->
+ ok;
+ _ ->
+ test_server:fail(get_valid_file)
+ end,
+ ?line error = erl_prim_loader:get_file("duuuuuuummmy_file"),
+ ?line error = erl_prim_loader:get_file(duuuuuuummmy_file),
+ ?line error = erl_prim_loader:get_file({dummy}),
+ ok.
+
+inet_existing(doc) -> ["Start a node using the 'inet' loading method, ",
+ "from an already started boot server."];
+inet_existing(Config) when is_list(Config) ->
+ case os:type() of
+ vxworks ->
+ {comment, "VxWorks: tested separately"};
+ _ ->
+ ?line Name = erl_prim_test_inet_existing,
+ ?line Host = host(),
+ ?line Cookie = atom_to_list(erlang:get_cookie()),
+ ?line IpStr = ip_str(Host),
+ ?line LFlag = get_loader_flag(os:type()),
+ ?line Args = LFlag ++ " -hosts " ++ IpStr ++
+ " -setcookie " ++ Cookie,
+ ?line {ok, BootPid} = erl_boot_server:start_link([Host]),
+ ?line {ok, Node} = start_node(Name, Args),
+ ?line {ok,[["inet"]]} = rpc:call(Node, init, get_argument, [loader]),
+ ?line stop_node(Node),
+ ?line unlink(BootPid),
+ ?line exit(BootPid, kill),
+ ok
+ end.
+
+inet_coming_up(doc) -> ["Start a node using the 'inet' loading method, ",
+ "but start the boot server afterwards."];
+inet_coming_up(Config) when is_list(Config) ->
+ case os:type() of
+ vxworks ->
+ {comment, "VxWorks: tested separately"};
+ _ ->
+ ?line Name = erl_prim_test_inet_coming_up,
+ ?line Cookie = atom_to_list(erlang:get_cookie()),
+ ?line Host = host(),
+ ?line IpStr = ip_str(Host),
+ ?line LFlag = get_loader_flag(os:type()),
+ ?line Args = LFlag ++
+ " -hosts " ++ IpStr ++
+ " -setcookie " ++ Cookie,
+ ?line {ok, Node} = start_node(Name, Args, [{wait, false}]),
+
+ %% Wait a while, then start boot server, and wait for node to start.
+ ?line test_server:sleep(test_server:seconds(6)),
+ io:format("erl_boot_server:start_link([~p]).", [Host]),
+ ?line {ok, BootPid} = erl_boot_server:start_link([Host]),
+ ?line wait_really_started(Node, 25),
+
+ %% Check loader argument, then cleanup.
+ ?line {ok,[["inet"]]} = rpc:call(Node, init, get_argument, [loader]),
+ ?line stop_node(Node),
+ ?line unlink(BootPid),
+ ?line exit(BootPid, kill),
+ ok
+ end.
+
+wait_really_started(Node, 0) ->
+ test_server:fail({not_booted,Node});
+wait_really_started(Node, N) ->
+ case rpc:call(Node, init, get_status, []) of
+ {started, _} ->
+ ok;
+ _ ->
+ test_server:sleep(1000),
+ wait_really_started(Node, N - 1)
+ end.
+
+inet_disconnects(doc) -> ["Start a node using the 'inet' loading method, ",
+ "then lose the connection."];
+inet_disconnects(Config) when is_list(Config) ->
+ case os:type() of
+ vxworks ->
+ {comment, "VxWorks: tested separately"};
+ _ ->
+ ?line Name = erl_prim_test_inet_disconnects,
+ ?line Host = host(),
+ ?line Cookie = atom_to_list(erlang:get_cookie()),
+ ?line IpStr = ip_str(Host),
+ ?line LFlag = get_loader_flag(os:type()),
+ ?line Args = LFlag ++ " -hosts " ++ IpStr ++
+ " -setcookie " ++ Cookie,
+
+ ?line {ok, BootPid} = erl_boot_server:start([Host]),
+ Self = self(),
+ %% This process shuts down the boot server during loading.
+ ?line Stopper = spawn_link(fun() -> stop_boot(BootPid, Self) end),
+ ?line receive
+ {Stopper,ready} -> ok
+ end,
+
+ %% Let the loading begin...
+ ?line {ok, Node} = start_node(Name, Args, [{wait, false}]),
+
+ %% When the stopper is ready, the slave node should be
+ %% looking for a boot server again.
+ receive
+ {Stopper,ok} ->
+ ok;
+ {Stopper,{error,Reason}} ->
+ ?line ?t:fail(Reason)
+ after 60000 ->
+ ?line ?t:fail(stopper_died)
+ end,
+
+ %% Start new boot server to see that loading is continued.
+ ?line {ok, BootPid2} = erl_boot_server:start_link([Host]),
+ ?line wait_really_started(Node, 25),
+ ?line {ok,[["inet"]]} = rpc:call(Node, init, get_argument, [loader]),
+ ?line stop_node(Node),
+ ?line unlink(BootPid2),
+ ?line exit(BootPid2, kill),
+ ok
+ end.
+
+%% Trace boot server calls and stop the server before loading is finished.
+stop_boot(BootPid, Super) ->
+ erlang:trace(all, true, [call]),
+ 1 = erlang:trace_pattern({erl_boot_server,send_file_result,3}, true, [local]),
+ BootRef = erlang:monitor(process, BootPid),
+ Super ! {self(),ready},
+ Result = get_calls(100, BootPid),
+ exit(BootPid, kill),
+ erlang:trace_pattern({erl_boot_server,send_file_result,3}, false, [local]),
+ erlang:trace(all, false, [call]),
+ receive
+ {'DOWN',BootRef,_,_, killed} -> ok
+ end,
+ Super ! {self(),Result}.
+
+get_calls(0, _) ->
+ ok;
+get_calls(Count, Pid) ->
+ receive
+ {trace,_,call,_MFA} ->
+ get_calls(Count-1, Pid)
+ after 10000 ->
+ {error,{trace_msg_timeout,Count}}
+ end.
+
+multiple_slaves(doc) ->
+ ["Start nodes in parallell, all using the 'inet' loading method, ",
+ "verify that the boot server manages"];
+multiple_slaves(Config) when is_list(Config) ->
+ case os:type() of
+ vxworks ->
+ {comment, "VxWorks: tested separately"};
+ {ose,_} ->
+ {comment, "OSE: multiple nodes not supported"};
+ _ ->
+ ?line Name = erl_prim_test_multiple_slaves,
+ ?line Host = host(),
+ ?line Cookie = atom_to_list(erlang:get_cookie()),
+ ?line IpStr = ip_str(Host),
+ ?line LFlag = get_loader_flag(os:type()),
+ ?line Args = LFlag ++ " -hosts " ++ IpStr ++
+ " -setcookie " ++ Cookie,
+
+ NoOfNodes = 10, % no of slave nodes to be started
+
+ NamesAndNodes =
+ lists:map(fun(N) ->
+ NameN = atom_to_list(Name) ++
+ integer_to_list(N),
+ NodeN = NameN ++ "@" ++ Host,
+ {list_to_atom(NameN),list_to_atom(NodeN)}
+ end, lists:seq(1, NoOfNodes)),
+
+ ?line Nodes = start_multiple_nodes(NamesAndNodes, Args, []),
+
+ %% "queue up" the nodes to wait for the boot server to respond
+ %% (note: test_server supervises each node start by accept()
+ %% on a socket, the timeout value for the accept has to be quite
+ %% long for this test to work).
+ ?line test_server:sleep(test_server:seconds(5)),
+ %% start the code loading circus!
+ ?line {ok,BootPid} = erl_boot_server:start_link([Host]),
+ %% give the nodes a chance to boot up before attempting to stop them
+ ?line test_server:sleep(test_server:seconds(10)),
+
+ ?line wait_and_shutdown(lists:reverse(Nodes), 30),
+
+ ?line unlink(BootPid),
+ ?line exit(BootPid, kill),
+ ok
+ end.
+
+start_multiple_nodes([{Name,Node} | NNs], Args, Started) ->
+ ?line {ok,Node} = start_node(Name, Args, [{wait, false}]),
+ start_multiple_nodes(NNs, Args, [Node | Started]);
+start_multiple_nodes([], _, Nodes) ->
+ Nodes.
+
+wait_and_shutdown([Node | Nodes], Tries) ->
+ ?line wait_really_started(Node, Tries),
+ ?line {ok,[["inet"]]} = rpc:call(Node, init, get_argument, [loader]),
+ ?line stop_node(Node),
+ wait_and_shutdown(Nodes, Tries);
+wait_and_shutdown([], _) ->
+ ok.
+
+
+file_requests(suite) -> {req, [{local_slave_nodes, 1}, {time, 10}]};
+file_requests(doc) -> ["Start a node using the 'inet' loading method, ",
+ "verify that the boot server responds to file requests."];
+file_requests(Config) when is_list(Config) ->
+ ?line {ok, Node, BootPid} = complete_start_node(erl_prim_test_file_req),
+
+ %% compare with results from file server calls (the
+ %% boot server uses the same file sys and cwd)
+ {ok,Files} = file:list_dir("."),
+ ?line {ok,Files} = rpc:call(Node, erl_prim_loader, list_dir, ["."]),
+ {ok,Info} = file:read_file_info("test_server.beam"),
+ ?line {ok,Info} = rpc:call(Node, erl_prim_loader, read_file_info, ["test_server.beam"]),
+ {ok,Cwd} = file:get_cwd(),
+ ?line {ok,Cwd} = rpc:call(Node, erl_prim_loader, get_cwd, []),
+ case file:get_cwd("C:") of
+ {error,enotsup} ->
+ ok;
+ {ok,DCwd} ->
+ ?line {ok,DCwd} = rpc:call(Node, erl_prim_loader, get_cwd, ["C:"])
+ end,
+
+ ?line stop_node(Node),
+ ?line unlink(BootPid),
+ ?line exit(BootPid, kill),
+ ok.
+
+complete_start_node(Name) ->
+ ?line Host = host(),
+ ?line Cookie = atom_to_list(erlang:get_cookie()),
+ ?line IpStr = ip_str(Host),
+ ?line LFlag = get_loader_flag(os:type()),
+ ?line Args = LFlag ++ " -hosts " ++ IpStr ++
+ " -setcookie " ++ Cookie,
+
+ ?line {ok,BootPid} = erl_boot_server:start_link([Host]),
+
+ ?line {ok,Node} = start_node(Name, Args),
+ ?line wait_really_started(Node, 25),
+ {ok, Node, BootPid}.
+
+local_archive(suite) ->
+ [];
+local_archive(doc) ->
+ ["Read files from local archive."];
+local_archive(Config) when is_list(Config) ->
+ PrivDir = ?config(priv_dir, Config),
+ KernelDir = filename:basename(code:lib_dir(kernel)),
+ Archive = filename:join([PrivDir, KernelDir ++ init:archive_extension()]),
+ file:delete(Archive),
+ ?line {ok, Archive} = create_archive(Archive, [KernelDir]),
+
+ Node = node(),
+ BeamName = "inet.beam",
+ ?line ok = test_archive(Node, Archive, KernelDir, BeamName),
+ ?line ok = rpc:call(Node, erl_prim_loader, release_archives, []),
+
+ ?line ok = file:delete(Archive),
+ ok.
+
+remote_archive(suite) ->
+ {req, [{local_slave_nodes, 1}, {time, 10}]};
+remote_archive(doc) ->
+ ["Read files from remote archive."];
+remote_archive(Config) when is_list(Config) ->
+ PrivDir = ?config(priv_dir, Config),
+ KernelDir = filename:basename(code:lib_dir(kernel)),
+ Archive = filename:join([PrivDir, KernelDir ++ init:archive_extension()]),
+ file:delete(Archive),
+ ?line {ok, Archive} = create_archive(Archive, [KernelDir]),
+
+ ?line {ok, Node, BootPid} = complete_start_node(remote_archive),
+
+ BeamName = "inet.beam",
+ ?line ok = test_archive(Node, Archive, KernelDir, BeamName),
+
+ ?line stop_node(Node),
+ ?line unlink(BootPid),
+ ?line exit(BootPid, kill),
+ ok.
+
+primary_archive(suite) ->
+ {req, [{local_slave_nodes, 1}, {time, 10}]};
+primary_archive(doc) ->
+ ["Read files from primary archive."];
+primary_archive(Config) when is_list(Config) ->
+ %% Copy the orig files to priv_dir
+ PrivDir = ?config(priv_dir, Config),
+ Archive = filename:join([PrivDir, "primary_archive.zip"]),
+ file:delete(Archive),
+ DataDir = ?config(data_dir, Config),
+ ?line {ok, _} = zip:create(Archive, ["primary_archive"],
+ [{compress, []}, {cwd, DataDir}]),
+ ?line {ok, _} = zip:extract(Archive, [{cwd, PrivDir}]),
+ TopDir = filename:join([PrivDir, "primary_archive"]),
+
+ %% Compile the code
+ DictDir = "primary_archive_dict-1.0",
+ DummyDir = "primary_archive_dummy",
+ ?line ok = compile_app(TopDir, DictDir),
+ ?line ok = compile_app(TopDir, DummyDir),
+
+ %% Create the archive
+ {ok, TopFiles} = file:list_dir(TopDir),
+ ?line {ok, {_, ArchiveBin}} = zip:create(Archive, TopFiles,
+ [memory, {compress, []}, {cwd, TopDir}]),
+
+ %% Use temporary node to simplify cleanup
+ ?line Cookie = atom_to_list(erlang:get_cookie()),
+ ?line Args = " -setcookie " ++ Cookie,
+ ?line {ok,Node} = start_node(primary_archive, Args),
+ ?line wait_really_started(Node, 25),
+
+ %% Set primary archive
+ ?line {_,_,_} = rpc:call(Node, erlang, date, []),
+ ?line {ok, Ebins} = rpc:call(Node, erl_prim_loader, set_primary_archive, [Archive, ArchiveBin]),
+ ExpectedEbins = [Archive, DictDir ++ "/ebin", DummyDir ++ "/ebin"],
+ io:format("ExpectedEbins: ~p\n", [ExpectedEbins]),
+ ?line ExpectedEbins = lists:sort(Ebins),
+
+ ?line {ok, TopFiles2} = rpc:call(Node, erl_prim_loader, list_dir, [Archive]),
+ ?line [DictDir, DummyDir] = lists:sort(TopFiles2),
+ BeamName = "primary_archive_dict_app.beam",
+ ?line ok = test_archive(Node, Archive, DictDir, BeamName),
+
+ ?line {ok, []} = rpc:call(Node, erl_prim_loader, set_primary_archive, [undefined, undefined]),
+
+ ?line stop_node(Node),
+ ?line ok = file:delete(Archive),
+ ok.
+
+test_archive(Node, TopDir, AppDir, BeamName) ->
+ %% List dir
+ io:format("test_archive: ~p\n", [rpc:call(Node, erl_prim_loader, list_dir, [TopDir])]),
+ ?line {ok, TopFiles} = rpc:call(Node, erl_prim_loader, list_dir, [TopDir]),
+ ?line true = lists:member(AppDir, TopFiles),
+ AbsAppDir = TopDir ++ "/" ++ AppDir,
+ ?line {ok, AppFiles} = rpc:call(Node, erl_prim_loader, list_dir, [AbsAppDir]),
+ ?line true = lists:member("ebin", AppFiles),
+ Ebin = AbsAppDir ++ "/ebin",
+ ?line {ok, EbinFiles} = rpc:call(Node, erl_prim_loader, list_dir, [Ebin]),
+ Beam = Ebin ++ "/" ++ BeamName,
+ ?line true = lists:member(BeamName, EbinFiles),
+ ?line error = rpc:call(Node, erl_prim_loader, list_dir, [TopDir ++ "/no_such_file"]),
+ ?line error = rpc:call(Node, erl_prim_loader, list_dir, [TopDir ++ "/ebin/no_such_file"]),
+
+ %% File info
+ ?line {ok, #file_info{type = directory}} =
+ rpc:call(Node, erl_prim_loader, read_file_info, [TopDir]),
+ ?line {ok, #file_info{type = directory}} =
+ rpc:call(Node, erl_prim_loader, read_file_info, [Ebin]),
+ ?line {ok, #file_info{type = regular} = FI} =
+ rpc:call(Node, erl_prim_loader, read_file_info, [Beam]),
+ ?line error = rpc:call(Node, erl_prim_loader, read_file_info, [TopDir ++ "/no_such_file"]),
+ ?line error = rpc:call(Node, erl_prim_loader, read_file_info, [TopDir ++ "/ebin/no_such_file"]),
+
+ %% Get file
+ ?line {ok, Bin, Beam} = rpc:call(Node, erl_prim_loader, get_file, [Beam]),
+ ?line if
+ FI#file_info.size =:= byte_size(Bin) -> ok;
+ true -> exit({FI#file_info.size, byte_size(Bin)})
+ end,
+ ?line error = rpc:call(Node, erl_prim_loader, get_file, ["/no_such_file"]),
+ ?line error = rpc:call(Node, erl_prim_loader, get_file, ["/ebin/no_such_file"]),
+ ok.
+
+create_archive(Archive, AppDirs) ->
+ LibDir = code:lib_dir(),
+ Opts = [{compress, []}, {cwd, LibDir}],
+ io:format("zip:create(~p,\n\t~p,\n\t~p).\n", [Archive, AppDirs, Opts]),
+ zip:create(Archive, AppDirs, Opts).
+
+%% Misc. functions
+
+ip_str({A, B, C, D}) ->
+ lists:concat([A, ".", B, ".", C, ".", D]);
+ip_str(Host) ->
+ {ok,Ip} = inet:getaddr(Host, inet),
+ ip_str(Ip).
+
+start_node(Name, Args) ->
+ start_node(Name, Args, []).
+
+start_node(Name, Args, Opts) ->
+ Opts2 = [{args, Args}|Opts],
+ io:format("test_server:start_node(~p, peer, ~p).\n",
+ [Name, Opts2]),
+ Res = test_server:start_node(Name, peer, Opts2),
+ io:format("start_node -> ~p\n", [Res]),
+ Res.
+
+host() ->
+ {ok,Host} = inet:gethostname(),
+ Host.
+
+stop_node(Node) ->
+ test_server:stop_node(Node).
+
+get_loader_flag({ose,_}) ->
+ " -loader ose_inet ";
+get_loader_flag(_) ->
+ " -loader inet ".
+
+compile_app(TopDir, AppName) ->
+ AppDir = filename:join([TopDir, AppName]),
+ SrcDir = filename:join([AppDir, "src"]),
+ OutDir = filename:join([AppDir, "ebin"]),
+ ?line {ok, Files} = file:list_dir(SrcDir),
+ compile_files(Files, SrcDir, OutDir).
+
+compile_files([File | Files], SrcDir, OutDir) ->
+ case filename:extension(File) of
+ ".erl" ->
+ AbsFile = filename:join([SrcDir, File]),
+ case compile:file(AbsFile, [{outdir, OutDir}]) of
+ {ok, _Mod} ->
+ compile_files(Files, SrcDir, OutDir);
+ Error ->
+ {compilation_error, AbsFile, OutDir, Error}
+ end;
+ _ ->
+ compile_files(Files, SrcDir, OutDir)
+ end;
+compile_files([], _, _) ->
+ ok.
+