%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 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(nif_SUITE).
%%-define(line_trace,true).
-include("test_server.hrl").
-export([all/1, fin_per_testcase/2, basic/1, reload/1, upgrade/1, heap_frag/1,
types/1, many_args/1, neg/1]).
-export([many_args_100/100]).
-define(nif_stub,nif_stub_error(?LINE)).
all(suite) ->
[basic, reload, upgrade, heap_frag, types, many_args, neg].
fin_per_testcase(_Func, _Config) ->
P1 = code:purge(nif_mod),
Del = code:delete(nif_mod),
P2 = code:purge(nif_mod),
io:format("fin purged=~p, deleted=~p and then purged=~p\n",[P1,Del,P2]).
basic(doc) -> ["Basic smoke test of load_nif and a simple NIF call"];
basic(suite) -> [];
basic(Config) when is_list(Config) ->
ensure_lib_loaded(Config),
?line true = (lib_version() =/= undefined),
?line [{load,1,1,101},{lib_version,1,2,102}] = call_history(),
?line [] = call_history(),
?line [?MODULE] = erlang:system_info(taints),
ok.
reload(doc) -> ["Test reload callback in nif lib"];
reload(suite) -> [];
reload(Config) when is_list(Config) ->
ensure_lib_loaded(Config),
?line Data = ?config(data_dir, Config),
?line File = filename:join(Data, "nif_mod"),
?line {ok,nif_mod,Bin} = compile:file(File, [binary,return_errors]),
?line {module,nif_mod} = erlang:load_module(nif_mod,Bin),
?line nif_mod:load_nif_lib(Config, 1),
?line hold_nif_mod_priv_data(nif_mod:get_priv_data_ptr()),
?line [{load,1,1,101},{get_priv_data_ptr,1,2,102}] = nif_mod_call_history(),
?line nif_mod:load_nif_lib(Config, 2),
?line 2 = nif_mod:lib_version(),
?line [{reload,2,1,201},{lib_version,2,2,202}] = nif_mod_call_history(),
?line nif_mod:load_nif_lib(Config, 1),
?line 1 = nif_mod:lib_version(),
?line [{reload,1,1,101},{lib_version,1,2,102}] = nif_mod_call_history(),
?line true = erlang:delete_module(nif_mod),
?line [] = nif_mod_call_history(),
%%?line false= check_process_code(Pid, nif_mod),
?line true = erlang:purge_module(nif_mod),
?line [{unload,1,3,103}] = nif_mod_call_history(),
?line [?MODULE, nif_mod] = erlang:system_info(taints),
ok.
upgrade(doc) -> ["Test upgrade callback in nif lib"];
upgrade(suite) -> [];
upgrade(Config) when is_list(Config) ->
ensure_lib_loaded(Config),
?line Data = ?config(data_dir, Config),
?line File = filename:join(Data, "nif_mod"),
?line {ok,nif_mod,Bin} = compile:file(File, [binary,return_errors]),
?line {module,nif_mod} = erlang:load_module(nif_mod,Bin),
?line nif_mod:load_nif_lib(Config, 1),
?line {Pid,MRef} = nif_mod:start(),
?line 1 = call(Pid,lib_version),
?line hold_nif_mod_priv_data(nif_mod:get_priv_data_ptr()),
?line [{load,1,1,101},{lib_version,1,2,102},{get_priv_data_ptr,1,3,103}] = nif_mod_call_history(),
%% Module upgrade with same lib-version
?line {module,nif_mod} = erlang:load_module(nif_mod,Bin),
?line undefined = nif_mod:lib_version(),
?line 1 = call(Pid,lib_version),
?line [{lib_version,1,4,104}] = nif_mod_call_history(),
?line nif_mod:load_nif_lib(Config, 1),
?line 1 = nif_mod:lib_version(),
?line [{upgrade,1,5,105},{lib_version,1,6,106}] = nif_mod_call_history(),
?line upgraded = call(Pid,upgrade),
?line false = check_process_code(Pid, nif_mod),
?line true = erlang:purge_module(nif_mod),
?line [{unload,1,7,107}] = nif_mod_call_history(),
?line 1 = nif_mod:lib_version(),
?line [{lib_version,1,8,108}] = nif_mod_call_history(),
?line true = erlang:delete_module(nif_mod),
?line [] = nif_mod_call_history(),
?line Pid ! die,
?line {'DOWN', MRef, process, Pid, normal} = receive_any(),
?line false = check_process_code(Pid, nif_mod),
?line true = erlang:purge_module(nif_mod),
?line [{unload,1,9,109}] = nif_mod_call_history(),
%% Module upgrade with different lib version
?line {module,nif_mod} = erlang:load_module(nif_mod,Bin),
?line undefined = nif_mod:lib_version(),
?line {Pid2,MRef2} = nif_mod:start(),
?line undefined = call(Pid2,lib_version),
?line nif_mod:load_nif_lib(Config, 1),
?line hold_nif_mod_priv_data(nif_mod:get_priv_data_ptr()),
?line 1 = call(Pid2,lib_version),
?line [{load,1,1,101},{get_priv_data_ptr,1,2,102},{lib_version,1,3,103}] = nif_mod_call_history(),
?line {module,nif_mod} = erlang:load_module(nif_mod,Bin),
?line undefined = nif_mod:lib_version(),
?line [] = nif_mod_call_history(),
?line 1 = call(Pid2,lib_version),
?line [{lib_version,1,4,104}] = nif_mod_call_history(),
?line nif_mod:load_nif_lib(Config, 2),
?line 2 = nif_mod:lib_version(),
?line [{upgrade,2,1,201},{lib_version,2,2,202}] = nif_mod_call_history(),
?line 1 = call(Pid2,lib_version),
?line [{lib_version,1,5,105}] = nif_mod_call_history(),
?line upgraded = call(Pid2,upgrade),
?line false = check_process_code(Pid2, nif_mod),
?line true = erlang:purge_module(nif_mod),
?line [{unload,1,6,106}] = nif_mod_call_history(),
?line 2 = nif_mod:lib_version(),
?line [{lib_version,2,3,203}] = nif_mod_call_history(),
?line true = erlang:delete_module(nif_mod),
?line [] = nif_mod_call_history(),
?line Pid2 ! die,
?line {'DOWN', MRef2, process, Pid2, normal} = receive_any(),
?line false= check_process_code(Pid2, nif_mod),
?line true = erlang:purge_module(nif_mod),
?line [{unload,2,4,204}] = nif_mod_call_history(),
?line [?MODULE, nif_mod] = erlang:system_info(taints),
ok.
heap_frag(doc) -> ["Test NIF building heap fragments"];
heap_frag(suite) -> [];
heap_frag(Config) when is_list(Config) ->
ensure_lib_loaded(Config),
heap_frag_do(1,1000000),
ok.
heap_frag_do(N, Max) when N > Max ->
ok;
heap_frag_do(N, Max) ->
io:format("Create list of length ~p\n",[N]),
L = lists:seq(1,N),
L = list_seq(N),
heap_frag_do(((N*5) div 4) + 1, Max).
types(doc) -> ["Type tests"];
types(suite) -> [];
types(Config) when is_list(Config) ->
ensure_lib_loaded(Config),
?line ok = type_test(),
lists:foreach(fun(Tpl) ->
Lst = erlang:tuple_to_list(Tpl),
Lst = tuple_2_list(Tpl)
end,
[{},{ok},{{}},{[],{}},{1,2,3,4,5}]),
Stuff = [[],{},0,0.0,(1 bsl 100),(fun()-> ok end),make_ref(),self()],
[eq_cmp(A,clone(B)) || A<-Stuff, B<-Stuff],
ok.
clone(X) ->
binary_to_term(term_to_binary(X)).
eq_cmp(A,B) ->
eq_cmp_do(A,B),
eq_cmp_do([A,B],[A,B]),
eq_cmp_do({A,B},{A,B}).
eq_cmp_do(A,B) ->
%%?t:format("compare ~p and ~p\n",[A,B]),
Eq = (A =:= B),
?line Eq = is_identical(A,B),
?line Cmp = if
A < B -> -1;
A == B -> 0;
A > B -> 1
end,
?line Cmp = case compare(A,B) of
C when is_integer(C), C < 0 -> -1;
0 -> 0;
C when is_integer(C) -> 1
end,
ok.
many_args(doc) -> ["Test NIF with many arguments"];
many_args(suite) -> [];
many_args(Config) when is_list(Config) ->
?line ensure_lib_loaded(Config ,1),
?line ok = apply(?MODULE,many_args_100,lists:seq(1,100)),
?line ok = many_args_100(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100),
ok.
neg(doc) -> ["Negative testing of load_nif"];
neg(suite) -> [];
neg(Config) when is_list(Config) ->
?line {'EXIT',{badarg,_}} = (catch erlang:load_nif(badarg, 0)),
?line {error,{load_failed,_}} = erlang:load_nif("pink_unicorn", 0),
?line Data = ?config(data_dir, Config),
?line File = filename:join(Data, "nif_mod"),
?line {ok,nif_mod,Bin} = compile:file(File, [binary,return_errors]),
?line {module,nif_mod} = erlang:load_module(nif_mod,Bin),
?line {error,{bad_lib,_}} = nif_mod:load_nif_lib(Config, no_init),
?line ok.
ensure_lib_loaded(Config) ->
ensure_lib_loaded(Config, 1).
ensure_lib_loaded(Config, Ver) ->
?line case lib_version() of
undefined ->
?line Path = ?config(data_dir, Config),
?line Lib = "nif_SUITE." ++ integer_to_list(Ver),
?line ok = erlang:load_nif(filename:join(Path,Lib), 0);
Ver when is_integer(Ver) ->
ok
end.
call(Pid,Cmd) ->
%%io:format("~p calling ~p with ~p\n",[self(), Pid, Cmd]),
Pid ! {self(), Cmd},
receive
{Pid,Reply} -> Reply
end.
receive_any() ->
receive M -> M end.
%% The NIFs:
lib_version() -> undefined.
call_history() -> ?nif_stub.
hold_nif_mod_priv_data(_Ptr) -> ?nif_stub.
nif_mod_call_history() -> ?nif_stub.
list_seq(_To) -> ?nif_stub.
type_test() -> ?nif_stub.
tuple_2_list(_) -> ?nif_stub.
is_identical(_,_) -> ?nif_stub.
compare(_,_) -> ?nif_stub.
many_args_100(_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_) -> ?nif_stub.
nif_stub_error(Line) ->
exit({nif_not_loaded,module,?MODULE,line,Line}).