From 2a96208cb00220f963e723ae0530492c5c70df27 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 11 Feb 2010 13:30:32 +0000 Subject: OTP-8335 Even more NIF features --- erts/emulator/test/nif_SUITE.erl | 608 ++++++++++++++++++++- erts/emulator/test/nif_SUITE_data/Makefile.src | 13 +- erts/emulator/test/nif_SUITE_data/head.txt | 5 + erts/emulator/test/nif_SUITE_data/nif_SUITE.c | 388 +++++++++++-- erts/emulator/test/nif_SUITE_data/nif_mod.c | 203 ++++++- erts/emulator/test/nif_SUITE_data/nif_mod.erl | 10 +- erts/emulator/test/nif_SUITE_data/nif_mod.h | 5 + erts/emulator/test/nif_SUITE_data/tail.txt | 5 + .../emulator/test/nif_SUITE_data/testcase_driver.h | 59 ++ erts/emulator/test/nif_SUITE_data/tester.c | 73 +++ erts/emulator/test/nif_SUITE_data/tester.erl | 13 + 11 files changed, 1303 insertions(+), 79 deletions(-) create mode 100644 erts/emulator/test/nif_SUITE_data/head.txt create mode 100644 erts/emulator/test/nif_SUITE_data/tail.txt create mode 100644 erts/emulator/test/nif_SUITE_data/testcase_driver.h create mode 100644 erts/emulator/test/nif_SUITE_data/tester.c create mode 100644 erts/emulator/test/nif_SUITE_data/tester.erl (limited to 'erts/emulator/test') diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl index e47161fcbc..522caec8f1 100644 --- a/erts/emulator/test/nif_SUITE.erl +++ b/erts/emulator/test/nif_SUITE.erl @@ -1,38 +1,50 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 2009-2010. 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). +%%-define(CHECK(Exp,Got), ?line check(Exp,Got,?LINE)). +-define(CHECK(Exp,Got), ?line Exp = Got). -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]). + types/1, many_args/1, binaries/1, get_string/1, get_atom/1, api_macros/1, + from_array/1, iolist_as_binary/1, resource/1, resource_takeover/1, + threading/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]. + [basic, reload, upgrade, heap_frag, types, many_args, binaries, get_string, + get_atom, api_macros, from_array, iolist_as_binary, resource, + resource_takeover, threading, neg]. + +%%init_per_testcase(_Case, Config) -> +%% ?line Dog = ?t:timetrap(?t:seconds(60*60*24)), +%% [{watchdog, Dog}|Config]. fin_per_testcase(_Func, _Config) -> + %%Dog = ?config(watchdog, Config), + %%?t:timetrap_cancel(Dog), P1 = code:purge(nif_mod), Del = code:delete(nif_mod), P2 = code:purge(nif_mod), @@ -51,6 +63,7 @@ basic(Config) when is_list(Config) -> reload(doc) -> ["Test reload callback in nif lib"]; reload(suite) -> []; reload(Config) when is_list(Config) -> + TmpMem = tmpmem(), ensure_lib_loaded(Config), ?line Data = ?config(data_dir, Config), @@ -58,16 +71,16 @@ reload(Config) when is_list(Config) -> ?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 ok = 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 ok = 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 ok = 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(), @@ -79,11 +92,13 @@ reload(Config) when is_list(Config) -> ?line [{unload,1,3,103}] = nif_mod_call_history(), ?line [?MODULE, nif_mod] = erlang:system_info(taints), + ?line verify_tmpmem(TmpMem), ok. upgrade(doc) -> ["Test upgrade callback in nif lib"]; upgrade(suite) -> []; upgrade(Config) when is_list(Config) -> + TmpMem = tmpmem(), ensure_lib_loaded(Config), ?line Data = ?config(data_dir, Config), @@ -91,7 +106,7 @@ upgrade(Config) when is_list(Config) -> ?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 ok = nif_mod:load_nif_lib(Config, 1), ?line {Pid,MRef} = nif_mod:start(), ?line 1 = call(Pid,lib_version), @@ -104,7 +119,7 @@ upgrade(Config) when is_list(Config) -> ?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 ok = 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(), @@ -131,7 +146,7 @@ upgrade(Config) when is_list(Config) -> ?line {Pid2,MRef2} = nif_mod:start(), ?line undefined = call(Pid2,lib_version), - ?line nif_mod:load_nif_lib(Config, 1), + ?line ok = 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(), @@ -142,7 +157,7 @@ upgrade(Config) when is_list(Config) -> ?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 ok = 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(), @@ -167,14 +182,17 @@ upgrade(Config) when is_list(Config) -> ?line [{unload,2,4,204}] = nif_mod_call_history(), ?line [?MODULE, nif_mod] = erlang:system_info(taints), + ?line verify_tmpmem(TmpMem), ok. heap_frag(doc) -> ["Test NIF building heap fragments"]; heap_frag(suite) -> []; heap_frag(Config) when is_list(Config) -> + TmpMem = tmpmem(), ensure_lib_loaded(Config), heap_frag_do(1,1000000), + ?line verify_tmpmem(TmpMem), ok. heap_frag_do(N, Max) when N > Max -> @@ -188,6 +206,7 @@ heap_frag_do(N, Max) -> types(doc) -> ["Type tests"]; types(suite) -> []; types(Config) when is_list(Config) -> + TmpMem = tmpmem(), ensure_lib_loaded(Config), ?line ok = type_test(), lists:foreach(fun(Tpl) -> @@ -197,6 +216,7 @@ types(Config) when is_list(Config) -> [{},{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], + ?line verify_tmpmem(TmpMem), ok. clone(X) -> @@ -227,17 +247,506 @@ eq_cmp_do(A,B) -> many_args(doc) -> ["Test NIF with many arguments"]; many_args(suite) -> []; many_args(Config) when is_list(Config) -> + TmpMem = tmpmem(), ?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), + ?line verify_tmpmem(TmpMem), ok. - +binaries(doc) -> ["Test NIF binary handling."]; +binaries(suite) -> []; +binaries(Config) when is_list(Config) -> + TmpMem = tmpmem(), + ?line ensure_lib_loaded(Config, 1), + ?line RefcBin = list_to_binary(lists:seq(1,255)), + ?line RefcBin = clone_bin(RefcBin), + ?line HeapBin = list_to_binary(lists:seq(1,20)), + ?line HeapBin = clone_bin(HeapBin), + ?line <<_:8,Sub1:6/binary,_/binary>> = RefcBin, + ?line <<_:8,Sub2:6/binary,_/binary>> = HeapBin, + ?line Sub1 = Sub2, + ?line Sub1 = clone_bin(Sub1), + ?line Sub2 = clone_bin(Sub2), + ?line <<_:9,Sub3:6/binary,_/bitstring>> = RefcBin, + ?line <<_:9,Sub4:6/binary,_/bitstring>> = HeapBin, + ?line Sub3 = Sub4, + ?line Sub3 = clone_bin(Sub3), + ?line Sub4 = clone_bin(Sub4), + %% When NIFs get bitstring support + %%?line <<_:8,Sub5:27/bitstring,_/bitstring>> = RefcBin, + %%?line <<_:8,Sub6:27/bitstring,_/bitstring>> = HeapBin, + %%?line Sub5 = Sub6, + %%?line Sub5 = clone_bin(Sub5), + %%?line Sub6 = clone_bin(Sub6), + %%?line <<_:9,Sub7:27/bitstring,_/bitstring>> = RefcBin, + %%?line <<_:9,Sub8:27/bitstring,_/bitstring>> = HeapBin, + %%?line Sub7 = Sub8, + %%?line Sub7 = clone_bin(Sub7), + %%?line Sub8 = clone_bin(Sub8), + %%?line <<>> = clone_bin(<<>>), + + <<_:8,SubBinA:200/binary,_/binary>> = RefcBin, + <<_:9,SubBinB:200/binary,_/bitstring>> = RefcBin, + <<_:8,SubBinC:17/binary,_/binary>> = HeapBin, + <<_:9,SubBinD:17/binary,_/bitstring>> = HeapBin, + test_make_sub_bin(RefcBin), + test_make_sub_bin(HeapBin), + test_make_sub_bin(SubBinA), + test_make_sub_bin(SubBinB), + test_make_sub_bin(SubBinC), + test_make_sub_bin(SubBinD), + + ?line verify_tmpmem(TmpMem), + ok. + +test_make_sub_bin(Bin) -> + Size = byte_size(Bin), + Rest10 = Size - 10, + Rest1 = Size - 1, + ?line Bin = make_sub_bin(Bin, 0, Size), + <<_:10/binary,Sub0:Rest10/binary>> = Bin, + ?line Sub0 = make_sub_bin(Bin, 10, Rest10), + <> = Bin, + ?line Sub1 = make_sub_bin(Bin, 0, 10), + <<_:7/binary,Sub2:10/binary,_/binary>> = Bin, + ?line Sub2 = make_sub_bin(Bin, 7, 10), + ?line <<>> = make_sub_bin(Bin, 0, 0), + ?line <<>> = make_sub_bin(Bin, 10, 0), + ?line <<>> = make_sub_bin(Bin, Rest1, 0), + ?line <<>> = make_sub_bin(Bin, Size, 0), + ok. + +get_string(doc) -> ["Test enif_get_string"]; +get_string(suite) -> []; +get_string(Config) when is_list(Config) -> + ?line ensure_lib_loaded(Config, 1), + ?line {7, <<"hejsan",0,_:3/binary>>} = string_to_bin("hejsan",10), + ?line {7, <<"hejsan",0,_>>} = string_to_bin("hejsan",8), + ?line {7, <<"hejsan",0>>} = string_to_bin("hejsan",7), + ?line {-6, <<"hejsa",0>>} = string_to_bin("hejsan",6), + ?line {-5, <<"hejs",0>>} = string_to_bin("hejsan",5), + ?line {-1, <<0>>} = string_to_bin("hejsan",1), + ?line {0, <<>>} = string_to_bin("hejsan",0), + ?line {1, <<0>>} = string_to_bin("",1), + ?line {0, <<>>} = string_to_bin("",0), + ok. + +get_atom(doc) -> ["Test enif_get_atom"]; +get_atom(suite) -> []; +get_atom(Config) when is_list(Config) -> + ?line ensure_lib_loaded(Config, 1), + ?line {7, <<"hejsan",0,_:3/binary>>} = atom_to_bin(hejsan,10), + ?line {7, <<"hejsan",0,_>>} = atom_to_bin(hejsan,8), + ?line {7, <<"hejsan",0>>} = atom_to_bin(hejsan,7), + ?line {0, <<_:6/binary>>} = atom_to_bin(hejsan,6), + ?line {0, <<>>} = atom_to_bin(hejsan,0), + ?line {1, <<0>>} = atom_to_bin('',1), + ?line {0, <<>>} = atom_to_bin('',0), + ok. + +api_macros(doc) -> ["Test macros enif_make_list and enif_make_tuple"]; +api_macros(suite) -> []; +api_macros(Config) when is_list(Config) -> + ?line ensure_lib_loaded(Config, 1), + Expected = {[lists:seq(1,N) || N <- lists:seq(1,9)], + [list_to_tuple(lists:seq(1,N)) || N <- lists:seq(1,9)] + }, + ?line Expected = macros(list_to_tuple(lists:seq(1,9))), + ok. + +from_array(doc) -> ["enif_make_[tuple|list]_from_array"]; +from_array(suite) -> []; +from_array(Config) when is_list(Config) -> + ?line ensure_lib_loaded(Config, 1), + lists:foreach(fun(Tpl) -> + Lst = tuple_to_list(Tpl), + ?line {Lst,Tpl} = tuple_2_list_and_tuple(Tpl) + end, + [{}, {1,2,3}, {[4,5],[],{},{6,7}}, {{}}, {[]}]), + ok. + +iolist_as_binary(doc) -> ["enif_inspect_iolist_as_binary"]; +iolist_as_binary(suite) -> []; +iolist_as_binary(Config) when is_list(Config) -> + ?line ensure_lib_loaded(Config, 1), + TmpMem = tmpmem(), + List = [<<"hejsan">>, <<>>, [], [17], [<<>>], + [127,128,255,0], + [1, 2, 3, <<"abc">>, [<<"def">>,4], 5, <<"ghi">>], + [1, 2, 3, <<"abc">>, [<<"def">>,4], 5 | <<"ghi">>]], + + lists:foreach(fun(IoL) -> + B1 = erlang:iolist_to_binary(IoL), + ?line B2 = iolist_2_bin(IoL), + ?line B1 = B2 + end, + List), + ?line verify_tmpmem(TmpMem), + ok. + +resource(doc) -> ["Test memory managed objects, aka 'resources'"]; +resource(suite) -> []; +resource(Config) when is_list(Config) -> + ?line ensure_lib_loaded(Config, 1), + ?line Type = get_resource_type(0), + resource_hugo(Type), + resource_otto(Type), + resource_new(Type), + resource_neg(Type), + ok. + +resource_hugo(Type) -> + DtorCall = resource_hugo_do(Type), + erlang:garbage_collect(), + ?line DtorCall = last_resource_dtor_call(), + ok. + +resource_hugo_do(Type) -> + HugoBin = <<"Hugo Hacker">>, + ?line HugoPtr = alloc_resource(Type, HugoBin), + ?line Hugo = make_resource(HugoPtr), + ?line <<>> = Hugo, + release_resource(HugoPtr), + erlang:garbage_collect(), + ?line {HugoPtr,HugoBin} = get_resource(Type,Hugo), + Pid = spawn_link(fun() -> + receive {Pid, Type, Resource, Ptr, Bin} -> + Pid ! {self(), got_it}, + receive {Pid, check_it} -> + ?line {Ptr,Bin} = get_resource(Type,Resource), + Pid ! {self(), ok} + end + end + end), + Pid ! {self(), Type, Hugo, HugoPtr, HugoBin}, + ?line {Pid, got_it} = receive_any(), + erlang:garbage_collect(), % just to make our ProcBin move in memory + Pid ! {self(), check_it}, + ?line {Pid, ok} = receive_any(), + ?line [] = last_resource_dtor_call(), + ?line {HugoPtr,HugoBin} = get_resource(Type,Hugo), + {HugoPtr, HugoBin, 1}. + +resource_otto(Type) -> + {OttoPtr, OttoBin} = resource_otto_do(Type), + erlang:garbage_collect(), + ?line [] = last_resource_dtor_call(), + release_resource(OttoPtr), + ?line {OttoPtr,OttoBin,1} = last_resource_dtor_call(), + ok. + +resource_otto_do(Type) -> + OttoBin = <<"Otto Ordonnans">>, + ?line OttoPtr = alloc_resource(Type, OttoBin), + ?line Otto = make_resource(OttoPtr), + ?line <<>> = Otto, + %% forget resource term but keep referenced by NIF + {OttoPtr, OttoBin}. + +resource_new(Type) -> + ?line {PtrB,BinB} = resource_new_do1(Type), + erlang:garbage_collect(), + ?line {PtrB,BinB,1} = last_resource_dtor_call(), + ok. + +resource_new_do1(Type) -> + ?line {{PtrA,BinA}, {ResB,PtrB,BinB}} = resource_new_do2(Type), + erlang:garbage_collect(), + ?line {PtrA,BinA,1} = last_resource_dtor_call(), + ?line {PtrB,BinB} = get_resource(Type, ResB), + %% forget ResB and make it garbage + {PtrB,BinB}. + +resource_new_do2(Type) -> + BinA = <<"NewA">>, + BinB = <<"NewB">>, + ?line ResA = make_new_resource(Type, BinA), + ?line ResB = make_new_resource(Type, BinB), + ?line <<>> = ResA, + ?line <<>> = ResB, + ?line {PtrA,BinA} = get_resource(Type, ResA), + ?line {PtrB,BinB} = get_resource(Type, ResB), + ?line true = (PtrA =/= PtrB), + ?line [] = last_resource_dtor_call(), + %% forget ResA and make it garbage + {{PtrA,BinA}, {ResB,PtrB,BinB}}. + +resource_neg(TypeA) -> + TypeB = get_resource_type(1), + Aptr = alloc_resource(TypeA, <<"Arnold">>), + Bptr = alloc_resource(TypeB, <<"Bobo">>), + ?line {'EXIT',{badarg,_}} = (catch get_resource(TypeA, Bptr)), + ?line {'EXIT',{badarg,_}} = (catch get_resource(TypeB, Aptr)), + ok. +-define(RT_CREATE,1). +-define(RT_TAKEOVER,2). + +resource_takeover(doc) -> ["Test resource takeover by module reload and upgrade"]; +resource_takeover(suite) -> []; +resource_takeover(Config) when is_list(Config) -> + TmpMem = tmpmem(), + ensure_lib_loaded(Config), + + ?line Data = ?config(data_dir, Config), + ?line File = filename:join(Data, "nif_mod"), + ?line {ok,nif_mod,ModBin} = compile:file(File, [binary,return_errors]), + ?line {module,nif_mod} = erlang:load_module(nif_mod,ModBin), + + ?line ok = nif_mod:load_nif_lib(Config, 1, + [{resource_type, 0, ?RT_CREATE, "resource_type_A",resource_dtor_A, + ?RT_CREATE}, + {resource_type, 1, ?RT_CREATE, "resource_type_null_A",null, + ?RT_CREATE}, + {resource_type, 2, ?RT_CREATE bor ?RT_TAKEOVER, "resource_type_A_null",resource_dtor_A, + ?RT_CREATE}, + {resource_type, 3, ?RT_CREATE, "resource_type_B_goneX",resource_dtor_B, + ?RT_CREATE}, + {resource_type, 4, ?RT_CREATE, "resource_type_null_goneX",null, + ?RT_CREATE}, + {resource_type, null, ?RT_TAKEOVER, "Pink unicorn", resource_dtor_A, + ?RT_TAKEOVER} + ]), + + ?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 {Holder, _MRef} = spawn_opt(fun resource_holder/0, [link, monitor]), + + {A1,BinA1} = make_resource(0,Holder,"A1"), + {A2,BinA2} = make_resource(0,Holder,"A2"), + {A3,BinA3} = make_resource(0,Holder,"A3"), + + {NA1,_BinNA1} = make_resource(1,Holder,"NA1"), + {NA2,BinNA2} = make_resource(1,Holder,"NA2"), + {NA3,_BinNA3} = make_resource(1,Holder,"NA3"), + + {AN1,BinAN1} = make_resource(2,Holder,"AN1"), + {AN2,_BinAN2} = make_resource(2,Holder,"AN2"), + {AN3,BinAN3} = make_resource(2,Holder,"AN3"), + + {BGX1,BinBGX1} = make_resource(3,Holder,"BGX1"), + {BGX2,BinBGX2} = make_resource(3,Holder,"BGX2"), + + {NGX1,_BinNGX1} = make_resource(4,Holder,"NGX1"), + {NGX2,_BinNGX2} = make_resource(4,Holder,"NGX2"), + + ?line [] = nif_mod_call_history(), + + ?line ok = forget_resource(A1), + ?line [{{resource_dtor_A_v1,BinA1},1,3,103}] = nif_mod_call_history(), + + ?line ok = forget_resource(NA1), + ?line [] = nif_mod_call_history(), % no dtor + + ?line ok = forget_resource(AN1), + ?CHECK([{{resource_dtor_A_v1,BinAN1},1,4,104}] , nif_mod_call_history()), + + ?line ok = forget_resource(BGX1), + ?CHECK([{{resource_dtor_B_v1,BinBGX1},1,5,105}], nif_mod_call_history()), + + ?line ok = forget_resource(NGX1), + ?CHECK([], nif_mod_call_history()), % no dtor + + ?line ok = nif_mod:load_nif_lib(Config, 2, + [{resource_type, 0, ?RT_TAKEOVER, "resource_type_A",resource_dtor_A, + ?RT_TAKEOVER}, + {resource_type, 1, ?RT_TAKEOVER bor ?RT_CREATE, "resource_type_null_A",resource_dtor_A, + ?RT_TAKEOVER}, + {resource_type, 2, ?RT_TAKEOVER, "resource_type_A_null",null, + ?RT_TAKEOVER}, + {resource_type, null, ?RT_TAKEOVER, "Pink unicorn", resource_dtor_A, + ?RT_TAKEOVER}, + {resource_type, null, ?RT_CREATE, "resource_type_B_goneX",resource_dtor_B, + ?RT_CREATE}, + {resource_type, null, ?RT_CREATE, "resource_type_null_goneX",null, + ?RT_CREATE}, + {resource_type, 3, ?RT_CREATE, "resource_type_B_goneY",resource_dtor_B, + ?RT_CREATE}, + {resource_type, 4, ?RT_CREATE, "resource_type_null_goneY",null, + ?RT_CREATE} + ]), + ?CHECK([{reload,2,1,201}], nif_mod_call_history()), + + ?line BinA2 = read_resource(0,A2), + ?line ok = forget_resource(A2), + ?CHECK([{{resource_dtor_A_v2,BinA2},2,2,202}], nif_mod_call_history()), + + ?line ok = forget_resource(NA2), + ?CHECK([{{resource_dtor_A_v2,BinNA2},2,3,203}], nif_mod_call_history()), + + ?line ok = forget_resource(AN2), + ?CHECK([], nif_mod_call_history()), % no dtor + + ?line ok = forget_resource(BGX2), % calling dtor in orphan library v1 still loaded + ?CHECK([{{resource_dtor_B_v1,BinBGX2},1,6,106}], nif_mod_call_history()), + % How to test that lib v1 is closed here? + + ?line ok = forget_resource(NGX2), + ?CHECK([], nif_mod_call_history()), % no dtor + + {BGY1,BinBGY1} = make_resource(3,Holder,"BGY1"), + {NGY1,_BinNGY1} = make_resource(4,Holder,"NGY1"), + + %% Module upgrade with same lib-version + ?line {module,nif_mod} = erlang:load_module(nif_mod,ModBin), + ?line undefined = nif_mod:lib_version(), + ?line ok = nif_mod:load_nif_lib(Config, 2, + [{resource_type, 2, ?RT_TAKEOVER, "resource_type_A",resource_dtor_B, + ?RT_TAKEOVER}, + {resource_type, 0, ?RT_TAKEOVER bor ?RT_CREATE, "resource_type_null_A",null, + ?RT_TAKEOVER}, + {resource_type, 1, ?RT_TAKEOVER, "resource_type_A_null",resource_dtor_A, + ?RT_TAKEOVER}, + {resource_type, null, ?RT_TAKEOVER, "Pink elephant", resource_dtor_A, + ?RT_TAKEOVER}, + {resource_type, 3, ?RT_CREATE, "resource_type_B_goneZ",resource_dtor_B, + ?RT_CREATE}, + {resource_type, 4, ?RT_CREATE, "resource_type_null_goneZ",null, + ?RT_CREATE} + ]), + + ?line 2 = nif_mod:lib_version(), + ?CHECK([{upgrade,2,4,204},{lib_version,2,5,205}], nif_mod_call_history()), + + ?line ok = forget_resource(A3), + ?CHECK([{{resource_dtor_B_v2,BinA3},2,6,206}], nif_mod_call_history()), + + ?line ok = forget_resource(NA3), + ?CHECK([], nif_mod_call_history()), + + ?line ok = forget_resource(AN3), + ?CHECK([{{resource_dtor_A_v2,BinAN3},2,7,207}], nif_mod_call_history()), + + {A4,BinA4} = make_resource(2,Holder, "A4"), + {NA4,BinNA4} = make_resource(0,Holder, "NA4"), + {AN4,_BinAN4} = make_resource(1,Holder, "AN4"), + + {BGZ1,BinBGZ1} = make_resource(3,Holder,"BGZ1"), + {NGZ1,_BinNGZ1} = make_resource(4,Holder,"NGZ1"), + + ?line false = code:purge(nif_mod), + ?line [] = nif_mod_call_history(), + + ?line ok = forget_resource(NGY1), + ?line [] = nif_mod_call_history(), + + ?line ok = forget_resource(BGY1), % calling dtor in orphan library v2 still loaded + ?line [{{resource_dtor_B_v2,BinBGY1},2,8,208},{unload,2,9,209}] = nif_mod_call_history(), + + %% Module upgrade with other lib-version + ?line {module,nif_mod} = erlang:load_module(nif_mod,ModBin), + ?line undefined = nif_mod:lib_version(), + ?line ok = nif_mod:load_nif_lib(Config, 1, + [{resource_type, 2, ?RT_TAKEOVER, "resource_type_A",resource_dtor_A, + ?RT_TAKEOVER}, + {resource_type, 0, ?RT_TAKEOVER bor ?RT_CREATE, "resource_type_null_A",resource_dtor_A, + ?RT_TAKEOVER}, + {resource_type, 1, ?RT_TAKEOVER, "resource_type_A_null",null, + ?RT_TAKEOVER}, + {resource_type, null, ?RT_TAKEOVER, "Mr Pink", resource_dtor_A, + ?RT_TAKEOVER} + ]), + + ?line 1 = nif_mod:lib_version(), + ?line [{upgrade,1,1,101},{lib_version,1,2,102}] = nif_mod_call_history(), + + %%?line false= check_process_code(Pid, nif_mod), + ?line false = code:purge(nif_mod), + %% no unload here as we still have instances with destructors + ?line [] = nif_mod_call_history(), + + ?line ok = forget_resource(BGZ1), % calling dtor in orphan library v2 still loaded + ?line [{{resource_dtor_B_v2,BinBGZ1},2,10,210},{unload,2,11,211}] = nif_mod_call_history(), + + ?line ok = forget_resource(NGZ1), + ?line [] = nif_mod_call_history(), + + ?line ok = forget_resource(A4), + ?line [{{resource_dtor_A_v1,BinA4},1,3,103}] = nif_mod_call_history(), + + ?line ok = forget_resource(NA4), + ?line [{{resource_dtor_A_v1,BinNA4},1,4,104}] = nif_mod_call_history(), + ?line ok = forget_resource(AN4), + ?line [] = nif_mod_call_history(), + + ?line [?MODULE, nif_mod] = erlang:system_info(taints), + ?line verify_tmpmem(TmpMem), + ok. + +make_resource(Type,Holder,Str) when is_list(Str) -> + Bin = list_to_binary(Str), + A1 = make_resource_do(Type,Holder,Bin), + ?line Bin = read_resource(Type,A1), + {A1,Bin}. + +make_resource_do(Type, Holder, Bin) -> + Holder ! {self(), make, Type, Bin}, + {Holder, make_ok, Id} = receive_any(), + {Holder,Id}. + +read_resource(Type, {Holder,Id}) -> + Holder ! {self(), get, Type, Id}, + {Holder, get_ok, Bin} = receive_any(), + Bin. + +forget_resource({Holder,Id}) -> + Holder ! {self(), forget, Id}, + {Holder, forget_ok, Id} = receive_any(), + ok. + + +resource_holder() -> + resource_holder([]). +resource_holder(List) -> + %%io:format("resource_holder waiting for msg\n", []), + Msg = receive_any(), + %%io:format("resource_holder got ~p with list = ~p\n", [Msg,List]), + case Msg of + {Pid, make, Type, Bin} -> + ?line Resource = nif_mod:make_new_resource(Type, Bin), + Id = {make_ref(),Bin}, + Pid ! {self(), make_ok, Id}, + resource_holder([{Id,Resource} | List]); + {Pid, get, Type, Id} -> + {Id,Resource} = lists:keyfind(Id, 1, List), + Pid ! {self(), get_ok, nif_mod:get_resource(Type, Resource)}, + resource_holder(List); + + {Pid, forget, Id} -> + NewList = lists:keydelete(Id, 1, List), + %%io:format("resource_holder forget: NewList = ~p\n", [NewList]), + resource_holder(Pid, {self(),forget_ok,Id}, NewList) + end. + +resource_holder(Pid,Reply,List) -> + erlang:garbage_collect(), + %%io:format("resource_holder GC'ed, now send ~p to ~p\n", [Reply,Pid]), + Pid ! Reply, + resource_holder(List). + + +threading(doc) -> ["Test the threading API functions (reuse tests from driver API)"]; +threading(Config) when is_list(Config) -> + ?line Data = ?config(data_dir, Config), + ?line File = filename:join(Data, "tester"), + ?line {ok,tester,ModBin} = compile:file(File, [binary,return_errors]), + ?line {module,tester} = erlang:load_module(tester,ModBin), + + ?line ok = tester:load_nif_lib(Config, "basic"), + ?line ok = tester:run(), + + ?line ok = tester:load_nif_lib(Config, "rwlock"), + ?line ok = tester:run(), + + ?line ok = tester:load_nif_lib(Config, "tsd"), + ?line ok = tester:run(). + neg(doc) -> ["Negative testing of load_nif"]; -neg(suite) -> []; neg(Config) when is_list(Config) -> + TmpMem = tmpmem(), ?line {'EXIT',{badarg,_}} = (catch erlang:load_nif(badarg, 0)), ?line {error,{load_failed,_}} = erlang:load_nif("pink_unicorn", 0), @@ -247,23 +756,61 @@ neg(Config) when is_list(Config) -> ?line {module,nif_mod} = erlang:load_module(nif_mod,Bin), ?line {error,{bad_lib,_}} = nif_mod:load_nif_lib(Config, no_init), + ?line verify_tmpmem(TmpMem), ?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); + ?line ok = erlang:load_nif(filename:join(Path,Lib), []); Ver when is_integer(Ver) -> ok end. +tmpmem() -> + case erlang:system_info({allocator,temp_alloc}) of + false -> undefined; + MemInfo -> + MSBCS = lists:foldl( + fun ({instance, _, L}, Acc) -> + {value,{_,MBCS}} = lists:keysearch(mbcs, 1, L), + {value,{_,SBCS}} = lists:keysearch(sbcs, 1, L), + [MBCS,SBCS | Acc] + end, + [], + MemInfo), + lists:foldl( + fun(L, {Bl0,BlSz0}) -> + {value,{_,Bl,_,_}} = lists:keysearch(blocks, 1, L), + {value,{_,BlSz,_,_}} = lists:keysearch(blocks_size, 1, L), + {Bl0+Bl,BlSz0+BlSz} + end, {0,0}, MSBCS) + end. + +verify_tmpmem(MemInfo) -> + %%wait_for_test_procs(), + case tmpmem() of + MemInfo -> + io:format("Tmp mem info: ~p", [MemInfo]), + case MemInfo of + {notsup,undefined} -> + %% Use 'erl +Mea max' to do more complete memory leak testing. + {comment,"Incomplete or no mem leak testing"}; + _ -> + ok + end; + Other -> + io:format("Expected: ~p", [MemInfo]), + io:format("Actual: ~p", [Other]), + ?t:fail() + end. + call(Pid,Cmd) -> %%io:format("~p calling ~p with ~p\n",[self(), Pid, Cmd]), Pid ! {self(), Cmd}, @@ -274,6 +821,15 @@ call(Pid,Cmd) -> receive_any() -> receive M -> M end. +%% check(Exp,Got,Line) -> +%% case Got of +%% Exp -> Exp; +%% _ -> +%% io:format("CHECK at ~p: Expected ~p but got ~p\n",[Line,Exp,Got]), +%% Got +%% end. + + %% The NIFs: lib_version() -> undefined. call_history() -> ?nif_stub. @@ -285,6 +841,20 @@ tuple_2_list(_) -> ?nif_stub. is_identical(_,_) -> ?nif_stub. compare(_,_) -> ?nif_stub. many_args_100(_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_) -> ?nif_stub. - +clone_bin(_) -> ?nif_stub. +make_sub_bin(_,_,_) -> ?nif_stub. +string_to_bin(_,_) -> ?nif_stub. +atom_to_bin(_,_) -> ?nif_stub. +macros(_) -> ?nif_stub. +tuple_2_list_and_tuple(_) -> ?nif_stub. +iolist_2_bin(_) -> ?nif_stub. +get_resource_type(_) -> ?nif_stub. +alloc_resource(_,_) -> ?nif_stub. +make_resource(_) -> ?nif_stub. +get_resource(_,_) -> ?nif_stub. +release_resource(_) -> ?nif_stub. +last_resource_dtor_call() -> ?nif_stub. +make_new_resource(_,_) -> ?nif_stub. + nif_stub_error(Line) -> exit({nif_not_loaded,module,?MODULE,line,Line}). diff --git a/erts/emulator/test/nif_SUITE_data/Makefile.src b/erts/emulator/test/nif_SUITE_data/Makefile.src index 6a8b4f1245..ab4ff77add 100644 --- a/erts/emulator/test/nif_SUITE_data/Makefile.src +++ b/erts/emulator/test/nif_SUITE_data/Makefile.src @@ -4,11 +4,22 @@ NIF_LIBS = nif_SUITE.1@dll@ \ nif_mod.2@dll@ \ nif_mod.3@dll@ -all: $(NIF_LIBS) +all: $(NIF_LIBS) basic@dll@ rwlock@dll@ tsd@dll@ @SHLIB_RULES@ $(NIF_LIBS): nif_SUITE.c nif_mod.c nif_mod.h +basic@dll@: tester.c testcase_driver.h +rwlock@dll@: tester.c testcase_driver.h + +tsd@dll@: tester.c testcase_driver.h + +DRIVER_DIR = ../erl_drv_thread_SUITE_data + +basic.c rwlock.c tsd.c: $(DRIVER_DIR)/$@ + cat head.txt > $@ + cat $(DRIVER_DIR)/$@ | sed -e 's/erl_drv_/enif_/g' -e 's/driver_/enif_/g' -e 's/ErlDrv/ErlNif/g' >> $@ + cat tail.txt >> $@ diff --git a/erts/emulator/test/nif_SUITE_data/head.txt b/erts/emulator/test/nif_SUITE_data/head.txt new file mode 100644 index 0000000000..4b9b44276f --- /dev/null +++ b/erts/emulator/test/nif_SUITE_data/head.txt @@ -0,0 +1,5 @@ +/* Do NOT edit this file!!! +** +** This is a NIF'ified COPY of the original in ../erl_drv_thread_SUITE_data/ +*/ + diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c index 4532062dce..7d05a9a880 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c +++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c @@ -15,6 +15,7 @@ typedef struct int ref_cnt; CallInfo* call_history; NifModPrivData* nif_mod; + union { ErlNifResourceType* t; long l; } rt_arr[2]; }PrivData; void add_call(ErlNifEnv* env, PrivData* data, const char* func_name) @@ -26,12 +27,29 @@ void add_call(ErlNifEnv* env, PrivData* data, const char* func_name) call->static_cntA = ++static_cntA; call->static_cntB = ++static_cntB; data->call_history = call; + call->arg = NULL; + call->arg_sz = 0; } #define ADD_CALL(FUNC_NAME) add_call(env, enif_get_data(env),FUNC_NAME) +static void* resource_dtor_last = NULL; +static unsigned resource_dtor_last_sz = 0; +static char resource_dtor_last_data[20]; +static int resource_dtor_cnt = 0; + +static void resource_dtor(ErlNifEnv* env, void* obj) +{ + resource_dtor_last = obj; + resource_dtor_cnt++; + resource_dtor_last_sz = enif_sizeof_resource(env, obj); + assert(resource_dtor_last_sz <= sizeof(resource_dtor_last_data)); + memcpy(resource_dtor_last_data, obj, resource_dtor_last_sz); +} + static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) { + /*ERL_NIF_TERM head, tail;*/ PrivData* data = enif_alloc(env, sizeof(PrivData)); assert(data != NULL); data->ref_cnt = 1; @@ -39,7 +57,26 @@ static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) data->nif_mod = NULL; add_call(env, data, "load"); - + + /* + head = load_info; + data->rt_cnt = 0; + for (head=load_info; enif_get_list_cell(env,load_info,&head,&tail); + head=tail) { + char buf[20]; + int n = enif_get_string(env,head,buf,sizeof(buf)); + assert(n > 0); + assert(i < sizeof(data->rt_arr)/sizeof(*data->rt_arr)); + data->rt_arr[data->rt_cnt++].t = enif_create_resource_type(env,buf,resource_dtor, + ERL_NIF_RT_CREATE,NULL); + } + assert(enif_is_empty_list(env,head)); + */ + data->rt_arr[0].t = enif_open_resource_type(env,"Gold",resource_dtor, + ERL_NIF_RT_CREATE,NULL); + data->rt_arr[1].t = enif_open_resource_type(env,"Silver",resource_dtor, + ERL_NIF_RT_CREATE,NULL); + *priv_data = data; return 0; } @@ -80,11 +117,19 @@ static ERL_NIF_TERM make_call_history(ErlNifEnv* env, CallInfo** headp) while (*headp != NULL) { CallInfo* call = *headp; - ERL_NIF_TERM tpl = enif_make_tuple(env, 4, - enif_make_atom(env,call->func_name), - enif_make_int(env,call->lib_ver), - enif_make_int(env,call->static_cntA), - enif_make_int(env,call->static_cntB)); + ERL_NIF_TERM func_term = enif_make_atom(env,call->func_name); + ERL_NIF_TERM tpl; + if (call->arg != NULL) { + ErlNifBinary arg_bin; + enif_alloc_binary(env, call->arg_sz, &arg_bin); + memcpy(arg_bin.data, call->arg, call->arg_sz); + func_term = enif_make_tuple2(env, func_term, + enif_make_binary(env, &arg_bin)); + } + tpl = enif_make_tuple4(env, func_term, + enif_make_int(env,call->lib_ver), + enif_make_int(env,call->static_cntA), + enif_make_int(env,call->static_cntB)); list = enif_make_list_cell(env, tpl, list); *headp = call->next; enif_free(env,call); @@ -117,11 +162,14 @@ static ERL_NIF_TERM hold_nif_mod_priv_data(ErlNifEnv* env, int argc, const ERL_N static ERL_NIF_TERM nif_mod_call_history(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { PrivData* data = (PrivData*) enif_get_data(env); - + ERL_NIF_TERM ret; if (data->nif_mod == NULL) { - return enif_make_string(env,"nif_mod pointer is NULL"); + return enif_make_string(env,"nif_mod pointer is NULL", ERL_NIF_LATIN1); } - return make_call_history(env,&data->nif_mod->call_history); + enif_mutex_lock(data->nif_mod->mtx); + ret = make_call_history(env, &data->nif_mod->call_history); + enif_mutex_unlock(data->nif_mod->mtx); + return ret; } static ERL_NIF_TERM list_seq(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) @@ -150,12 +198,34 @@ static int test_int(ErlNifEnv* env, int i1) return 1; } +static int test_uint(ErlNifEnv* env, unsigned i1) +{ + unsigned i2 = 0; + ERL_NIF_TERM int_term = enif_make_uint(env, i1); + if (!enif_get_uint(env,int_term, &i2) || i1 != i2) { + fprintf(stderr, "test_uint(%u) ...FAILED i2=%u\r\n", i1, i2); + return 0; + } + return 1; +} + +static int test_long(ErlNifEnv* env, long i1) +{ + long i2 = 0; + ERL_NIF_TERM int_term = enif_make_long(env, i1); + if (!enif_get_long(env,int_term, &i2) || i1 != i2) { + fprintf(stderr, "test_long(%ld) ...FAILED i2=%ld\r\n", i1, i2); + return 0; + } + return 1; +} + static int test_ulong(ErlNifEnv* env, unsigned long i1) { unsigned long i2 = 0; ERL_NIF_TERM int_term = enif_make_ulong(env, i1); if (!enif_get_ulong(env,int_term, &i2) || i1 != i2) { - fprintf(stderr, "SVERK: test_ulong(%lu) ...FAILED i2=%lu\r\n", i1, i2); + fprintf(stderr, "test_ulong(%lu) ...FAILED i2=%lu\r\n", i1, i2); return 0; } return 1; @@ -166,7 +236,7 @@ static int test_double(ErlNifEnv* env, double d1) double d2 = 0; ERL_NIF_TERM term = enif_make_double(env, d1); if (!enif_get_double(env,term, &d2) || d1 != d2) { - fprintf(stderr, "SVERK: test_double(%e) ...FAILED i2=%e\r\n", d1, d2); + fprintf(stderr, "test_double(%e) ...FAILED i2=%e\r\n", d1, d2); return 0; } return 1; @@ -180,32 +250,59 @@ static int test_double(ErlNifEnv* env, double d1) static ERL_NIF_TERM type_test(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { int i; - unsigned long u; + int sint; + unsigned uint; + long slong; + unsigned long ulong; double d; ERL_NIF_TERM atom, ref1, ref2; - i = INT_MIN; + sint = INT_MIN; do { - if (!test_int(env,i)) { + if (!test_int(env,sint)) { goto error; } - i += ~i / 3 + 1; - } while (i < 0); - i = INT_MAX; + sint += ~sint / 3 + 1; + } while (sint < 0); + sint = INT_MAX; do { - if (!test_int(env,i)) { + if (!test_int(env,sint)) { goto error; } - i -= i / 3 + 1; - } while (i >= 0); + sint -= sint / 3 + 1; + } while (sint >= 0); - u = ULONG_MAX; + slong = LONG_MIN; + do { + if (!test_long(env,slong)) { + goto error; + } + slong += ~slong / 3 + 1; + } while (slong < 0); + slong = LONG_MAX; + do { + if (!test_long(env,slong)) { + goto error; + } + slong -= slong / 3 + 1; + } while (slong >= 0); + + + uint = UINT_MAX; for (;;) { - if (!test_ulong(env,u)) { + if (!test_uint(env,uint)) { } - if (u == 0) break; - u -= u / 3 + 1; + if (uint == 0) break; + uint -= uint / 3 + 1; + } + ulong = ULONG_MAX; + for (;;) { + if (!test_ulong(env,ulong)) { + + } + if (ulong == 0) break; + ulong -= ulong / 3 + 1; } if (MAX_SMALL < INT_MAX) { /* 32-bit */ @@ -219,11 +316,17 @@ static ERL_NIF_TERM type_test(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[ goto error; } } + for (i=-10 ; i <= 10; i++) { + if (!test_uint(env,MAX_SMALL+i)) { + goto error; + } + } } assert((MAX_SMALL < INT_MAX) == (MIN_SMALL > INT_MIN)); - for (u=0 ; u < 10; u++) { - if (!test_ulong(env,MAX_SMALL+u) || !test_ulong(env,MAX_SMALL-u)) { + for (i=-10 ; i < 10; i++) { + if (!test_long(env,MAX_SMALL+i) || !test_ulong(env,MAX_SMALL+i) || + !test_long(env,MIN_SMALL+i)) { goto error; } } @@ -236,12 +339,12 @@ static ERL_NIF_TERM type_test(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[ if (!enif_make_existing_atom(env,"nif_SUITE", &atom) || !enif_is_identical(env,atom,enif_make_atom(env,"nif_SUITE"))) { - fprintf(stderr, "SVERK: nif_SUITE not an atom?\r\n"); + fprintf(stderr, "nif_SUITE not an atom?\r\n"); goto error; } for (i=2; i; i--) { if (enif_make_existing_atom(env,"nif_SUITE_pink_unicorn", &atom)) { - fprintf(stderr, "SVERK: pink unicorn exist?\r\n"); + fprintf(stderr, "pink unicorn exist?\r\n"); goto error; } } @@ -249,7 +352,8 @@ static ERL_NIF_TERM type_test(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[ ref2 = enif_make_ref(env); if (!enif_is_ref(env,ref1) || !enif_is_ref(env,ref2) || enif_is_identical(env,ref1,ref2) || enif_compare(env,ref1,ref2)==0) { - fprintf(stderr, "SVERK: strange refs?\r\n"); + fprintf(stderr, "strange refs?\r\n"); + goto error; } return enif_make_atom(env,"ok"); @@ -260,7 +364,7 @@ error: static ERL_NIF_TERM tuple_2_list(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { int arity = -1; - ERL_NIF_TERM* ptr; + const ERL_NIF_TERM* ptr; ERL_NIF_TERM list = enif_make_list(env,0); if (argc!=1 || !enif_get_tuple(env,argv[0],&arity,&ptr)) { @@ -304,6 +408,213 @@ badarg: return enif_make_badarg(env); } +static ERL_NIF_TERM clone_bin(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + ErlNifBinary ibin; + if (enif_inspect_binary(env,argv[0],&ibin)) { + ErlNifBinary obin; + enif_alloc_binary(env,ibin.size,&obin); + memcpy(obin.data,ibin.data,ibin.size); + /*enif_release_binary(env,&ibin);*/ + return enif_make_binary(env,&obin); + } + else { + return enif_make_badarg(env); + } +} + +static ERL_NIF_TERM make_sub_bin(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + int pos, size; + if (!enif_get_int(env,argv[1],&pos) || !enif_get_int(env,argv[2],&size)) { + return enif_make_badarg(env); + } + return enif_make_sub_binary(env,argv[0],pos,size); +} + +static ERL_NIF_TERM string_to_bin(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + ErlNifBinary obin; + unsigned size; + int n; + if (!enif_get_int(env,argv[1],(int*)&size) + || !enif_alloc_binary(env,size,&obin)) { + return enif_make_badarg(env); + } + n = enif_get_string(env, argv[0], (char*)obin.data, size, ERL_NIF_LATIN1); + return enif_make_tuple(env, 2, enif_make_int(env,n), + enif_make_binary(env,&obin)); +} + +static ERL_NIF_TERM atom_to_bin(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + ErlNifBinary obin; + unsigned size; + int n; + if (!enif_get_int(env,argv[1],(int*)&size) + || !enif_alloc_binary(env,size,&obin)) { + return enif_make_badarg(env); + } + n = enif_get_atom(env, argv[0], (char*)obin.data, size); + return enif_make_tuple(env, 2, enif_make_int(env,n), + enif_make_binary(env,&obin)); +} + +static ERL_NIF_TERM macros(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + const ERL_NIF_TERM* a; + ERL_NIF_TERM lists, tuples; + int arity; + if (!enif_get_tuple(env, argv[0], &arity, &a) || arity != 9) { + return enif_make_badarg(env); + } + + lists = enif_make_list(env,9, + enif_make_list1(env,a[0]), + enif_make_list2(env,a[0],a[1]), + enif_make_list3(env,a[0],a[1],a[2]), + enif_make_list4(env,a[0],a[1],a[2],a[3]), + enif_make_list5(env,a[0],a[1],a[2],a[3],a[4]), + enif_make_list6(env,a[0],a[1],a[2],a[3],a[4],a[5]), + enif_make_list7(env,a[0],a[1],a[2],a[3],a[4],a[5],a[6]), + enif_make_list8(env,a[0],a[1],a[2],a[3],a[4],a[5],a[6],a[7]), + enif_make_list9(env,a[0],a[1],a[2],a[3],a[4],a[5],a[6],a[7],a[8])); + tuples = enif_make_list(env,9, + enif_make_tuple1(env,a[0]), + enif_make_tuple2(env,a[0],a[1]), + enif_make_tuple3(env,a[0],a[1],a[2]), + enif_make_tuple4(env,a[0],a[1],a[2],a[3]), + enif_make_tuple5(env,a[0],a[1],a[2],a[3],a[4]), + enif_make_tuple6(env,a[0],a[1],a[2],a[3],a[4],a[5]), + enif_make_tuple7(env,a[0],a[1],a[2],a[3],a[4],a[5],a[6]), + enif_make_tuple8(env,a[0],a[1],a[2],a[3],a[4],a[5],a[6],a[7]), + enif_make_tuple9(env,a[0],a[1],a[2],a[3],a[4],a[5],a[6],a[7],a[8])); + return enif_make_tuple2(env,lists,tuples); +} + +static ERL_NIF_TERM tuple_2_list_and_tuple(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + const ERL_NIF_TERM* arr; + int arity; + if (!enif_get_tuple(env,argv[0],&arity,&arr)) { + return enif_make_badarg(env); + } + return enif_make_tuple2(env, + enif_make_list_from_array(env, arr, arity), + enif_make_tuple_from_array(env, arr, arity)); +} + +static ERL_NIF_TERM iolist_2_bin(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + ErlNifBinary obin; + if (!enif_inspect_iolist_as_binary(env, argv[0], &obin)) { + return enif_make_badarg(env); + } + return enif_make_binary(env,&obin); +} + +static ERL_NIF_TERM last_resource_dtor_call(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + ErlNifBinary bin; + ERL_NIF_TERM ret; + if (resource_dtor_last != NULL) { + enif_alloc_binary(env, resource_dtor_last_sz, &bin); + memcpy(bin.data, resource_dtor_last_data, resource_dtor_last_sz); + ret = enif_make_tuple3(env, + enif_make_long(env, (long)resource_dtor_last), + enif_make_binary(env, &bin), + enif_make_int(env, resource_dtor_cnt)); + } + else { + ret = enif_make_list(env,0); + } + resource_dtor_last = NULL; + resource_dtor_last_sz = 0; + resource_dtor_cnt = 0; + return ret; +} + +static ERL_NIF_TERM get_resource_type(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + PrivData* data = (PrivData*) enif_get_data(env); + int ix; + + if (!enif_get_int(env, argv[0], &ix) || ix >= 2) { + return enif_make_badarg(env); + } + return enif_make_long(env, data->rt_arr[ix].l); +} + +static ERL_NIF_TERM alloc_resource(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + ErlNifBinary data_bin; + union { ErlNifResourceType* t; long l;} type; + union { void* p; long l;} data; + if (!enif_get_long(env, argv[0], &type.l) + || !enif_inspect_binary(env, argv[1], &data_bin) + || (data.p = enif_alloc_resource(env, type.t, data_bin.size))==NULL) { + + return enif_make_badarg(env); + } + memcpy(data.p, data_bin.data, data_bin.size); + return enif_make_long(env, data.l); +} + +static ERL_NIF_TERM make_resource(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + union { void* p; long l; } data; + if (!enif_get_long(env, argv[0], &data.l)) { + return enif_make_badarg(env); + } + return enif_make_resource(env, data.p); +} + +static ERL_NIF_TERM make_new_resource(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + ErlNifBinary data_bin; + union { ErlNifResourceType* t; long l;} type; + void* data; + ERL_NIF_TERM ret; + if (!enif_get_long(env, argv[0], &type.l) + || !enif_inspect_binary(env, argv[1], &data_bin) + || (data = enif_alloc_resource(env, type.t, data_bin.size))==NULL) { + + return enif_make_badarg(env); + } + ret = enif_make_resource(env, data); + memcpy(data, data_bin.data, data_bin.size); + enif_release_resource(env, data); + return ret; +} + +static ERL_NIF_TERM get_resource(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + ErlNifBinary data_bin; + union { ErlNifResourceType* t; long l; } type; + union { void* p; long l; } data; + + if (!enif_get_long(env, argv[0], &type.l) + || !enif_get_resource(env, argv[1], type.t, &data.p)) { + return enif_make_badarg(env); + } + + enif_alloc_binary(env, enif_sizeof_resource(env,data.p), &data_bin); + memcpy(data_bin.data, data.p, data_bin.size); + return enif_make_tuple2(env, enif_make_long(env,data.l), + enif_make_binary(env, &data_bin)); +} + +static ERL_NIF_TERM release_resource(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + union { void* p; long l; } data; + if (!enif_get_long(env, argv[0], &data.l)) { + return enif_make_badarg(env); + } + enif_release_resource(env, data.p); + return enif_make_atom(env,"ok"); +} + + static ErlNifFunc nif_funcs[] = { {"lib_version", 0, lib_version}, @@ -315,7 +626,22 @@ static ErlNifFunc nif_funcs[] = {"tuple_2_list", 1, tuple_2_list}, {"is_identical",2,is_identical}, {"compare",2,compare}, - {"many_args_100", 100, many_args_100} + {"many_args_100", 100, many_args_100}, + {"clone_bin", 1, clone_bin}, + {"make_sub_bin", 3, make_sub_bin}, + {"string_to_bin", 2, string_to_bin}, + {"atom_to_bin", 2, atom_to_bin}, + {"macros", 1, macros}, + {"tuple_2_list_and_tuple",1,tuple_2_list_and_tuple}, + {"iolist_2_bin", 1, iolist_2_bin}, + {"get_resource_type", 1, get_resource_type}, + {"alloc_resource", 2, alloc_resource}, + {"make_resource", 1, make_resource}, + {"get_resource", 2, get_resource}, + {"release_resource", 1, release_resource}, + {"last_resource_dtor_call", 0, last_resource_dtor_call}, + {"make_new_resource", 2, make_new_resource} + }; ERL_NIF_INIT(nif_SUITE,nif_funcs,load,reload,upgrade,unload) diff --git a/erts/emulator/test/nif_SUITE_data/nif_mod.c b/erts/emulator/test/nif_SUITE_data/nif_mod.c index 2f2267cf78..c075b74c57 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_mod.c +++ b/erts/emulator/test/nif_SUITE_data/nif_mod.c @@ -1,51 +1,182 @@ #include "erl_nif.h" #include -#include +#include #include "nif_mod.h" +#define CHECK(X) ((void)((X) || (check_abort(__LINE__),1))) +#ifdef __GNUC__ +static void check_abort(unsigned line) __attribute__((noreturn)); +#endif +static void check_abort(unsigned line) +{ + enif_fprintf(stderr, "Test CHECK failed at %s:%u\r\n", + __FILE__, line); + abort(); +} static int static_cntA; /* zero by default */ static int static_cntB = NIF_LIB_VER * 100; -static void add_call(ErlNifEnv* env, NifModPrivData* data, const char* func_name) +static ERL_NIF_TERM am_true; +static ERL_NIF_TERM am_null; +static ERL_NIF_TERM am_resource_type; +static ERL_NIF_TERM am_resource_dtor_A; +static ERL_NIF_TERM am_resource_dtor_B; + +static void init(ErlNifEnv* env) +{ + am_true = enif_make_atom(env, "true"); + am_null = enif_make_atom(env, "null"); + am_resource_type = enif_make_atom(env, "resource_type"); + am_resource_dtor_A = enif_make_atom(env, "resource_dtor_A"); + am_resource_dtor_B = enif_make_atom(env, "resource_dtor_B"); +} + +static void add_call_with_arg(ErlNifEnv* env, NifModPrivData* data, const char* func_name, + const char* arg, int arg_sz) { - CallInfo* call = enif_alloc(env, sizeof(CallInfo)+strlen(func_name)); + CallInfo* call = enif_alloc(env, sizeof(CallInfo)+strlen(func_name) + arg_sz); strcpy(call->func_name, func_name); call->lib_ver = NIF_LIB_VER; call->static_cntA = ++static_cntA; call->static_cntB = ++static_cntB; + call->arg_sz = arg_sz; + if (arg != NULL) { + call->arg = call->func_name + strlen(func_name) + 1; + memcpy(call->arg, arg, arg_sz); + } + else { + call->arg = NULL; + } + enif_mutex_lock(data->mtx); call->next = data->call_history; data->call_history = call; + enif_mutex_unlock(data->mtx); +} + +static void add_call(ErlNifEnv* env, NifModPrivData* data,const char* func_name) +{ + add_call_with_arg(env, data, func_name, NULL, 0); +} + +#define ADD_CALL(FUNC_NAME) add_call(env, enif_priv_data(env),FUNC_NAME) + +#define STRINGIFY_(X) #X +#define STRINGIFY(X) STRINGIFY_(X) + +static void resource_dtor_A(ErlNifEnv* env, void* a) +{ + const char dtor_name[] = "resource_dtor_A_v" STRINGIFY(NIF_LIB_VER); + + add_call_with_arg(env, enif_priv_data(env), dtor_name, + a, enif_sizeof_resource(env, a)); +} + +static void resource_dtor_B(ErlNifEnv* env, void* a) +{ + const char dtor_name[] = "resource_dtor_B_v" STRINGIFY(NIF_LIB_VER); + + add_call_with_arg(env, enif_priv_data(env), dtor_name, + a, enif_sizeof_resource(env, a)); +} + +/* {resource_type, Ix|null, ErlNifResourceFlags in, "TypeName", dtor(A|B|null), ErlNifResourceFlags out}*/ +static void open_resource_type(ErlNifEnv* env, ERL_NIF_TERM op_tpl) +{ + NifModPrivData* data = enif_priv_data(env); + const ERL_NIF_TERM* arr; + int arity; + char rt_name[30]; + union { enum ErlNifResourceFlags e; int i; } flags, exp_res, got_res; + unsigned ix; + ErlNifResourceDtor* dtor; + ErlNifResourceType* got_ptr; + + CHECK(enif_get_tuple(env, op_tpl, &arity, &arr)); + CHECK(arity == 6); + CHECK(enif_is_identical(env, arr[0], am_resource_type)); + CHECK(enif_get_int(env, arr[2], &flags.i)); + CHECK(enif_get_string(env, arr[3], rt_name, sizeof(rt_name), ERL_NIF_LATIN1) > 0); + CHECK(enif_get_int(env, arr[5], &exp_res.i)); + + if (enif_is_identical(env, arr[4], am_null)) { + dtor = NULL; + } + else if (enif_is_identical(env, arr[4], am_resource_dtor_A)) { + dtor = resource_dtor_A; + } + else { + CHECK(enif_is_identical(env, arr[4], am_resource_dtor_B)); + dtor = resource_dtor_B; + } + + got_ptr = enif_open_resource_type(env, rt_name, dtor, + flags.e, &got_res.e); + + if (enif_get_uint(env, arr[1], &ix) && ix < RT_MAX && got_ptr != NULL) { + data->rt_arr[ix] = got_ptr; + } + else { + CHECK(enif_is_identical(env, arr[1], am_null)); + CHECK(got_ptr == NULL); + } + CHECK(got_res.e == exp_res.e); } -#define ADD_CALL(FUNC_NAME) add_call(env, enif_get_data(env),FUNC_NAME) +static void do_load_info(ErlNifEnv* env, ERL_NIF_TERM load_info) +{ + NifModPrivData* data = enif_priv_data(env); + ERL_NIF_TERM head, tail; + unsigned ix; + for (ix=0; ixrt_arr[ix] = NULL; + } + for (head = load_info; enif_get_list_cell(env, head, &head, &tail); + head = tail) { + + open_resource_type(env, head); + } + CHECK(enif_is_empty_list(env, head)); +} static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) { - NifModPrivData* data = enif_alloc(env, sizeof(NifModPrivData)); - assert(data != NULL); + NifModPrivData* data; + + init(env); + data = enif_alloc(env, sizeof(NifModPrivData)); + CHECK(data != NULL); + *priv_data = data; + data->mtx = enif_mutex_create("nif_mod_priv_data"); data->ref_cnt = 1; data->call_history = NULL; add_call(env, data, "load"); - data->calls = 0; - *priv_data = data; + do_load_info(env, load_info); + data->calls = 0; return 0; } static int reload(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) { + init(env); add_call(env, *priv_data, "reload"); + + do_load_info(env, load_info); return 0; } static int upgrade(ErlNifEnv* env, void** priv_data, void** old_priv_data, ERL_NIF_TERM load_info) { NifModPrivData* data = *old_priv_data; + init(env); add_call(env, data, "upgrade"); data->ref_cnt++; + *priv_data = *old_priv_data; + do_load_info(env, load_info); + return 0; } @@ -53,9 +184,13 @@ static void unload(ErlNifEnv* env, void* priv_data) { NifModPrivData* data = priv_data; add_call(env, data, "unload"); + enif_mutex_lock(data->mtx); if (--data->ref_cnt == 0) { + enif_mutex_unlock(data->mtx); + enif_mutex_destroy(data->mtx); enif_free(env, data); } + enif_mutex_unlock(data->mtx); } static ERL_NIF_TERM lib_version(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) @@ -64,35 +199,51 @@ static ERL_NIF_TERM lib_version(ErlNifEnv* env, int argc, const ERL_NIF_TERM arg return enif_make_int(env, NIF_LIB_VER); } -static ERL_NIF_TERM call_history(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +static ERL_NIF_TERM get_priv_data_ptr(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { - NifModPrivData* data = (NifModPrivData*) enif_get_data(env); - ERL_NIF_TERM list = enif_make_list(env, 0); /* NIL */ + ADD_CALL("get_priv_data_ptr"); + return enif_make_ulong(env, (unsigned long)enif_priv_data(env)); +} - while (data->call_history != NULL) { - CallInfo* call = data->call_history; - ERL_NIF_TERM tpl = enif_make_tuple(env, 2, - enif_make_atom(env,call->func_name), - enif_make_int(env,call->lib_ver)); - list = enif_make_list_cell(env, tpl, list); - data->call_history = call->next; - enif_free(env,call); +static ERL_NIF_TERM make_new_resource(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + NifModPrivData* data = (NifModPrivData*) enif_priv_data(env); + ErlNifBinary ibin; + char* a; + ERL_NIF_TERM ret; + unsigned ix; + if (!enif_get_uint(env, argv[0], &ix) || ix >= RT_MAX + || !enif_inspect_binary(env, argv[1], &ibin)) { + return enif_make_badarg(env); } - return list; + a = enif_alloc_resource(env, data->rt_arr[ix], ibin.size); + memcpy(a, ibin.data, ibin.size); + ret = enif_make_resource(env, a); + enif_release_resource(env, a); + return ret; } -static ERL_NIF_TERM get_priv_data_ptr(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +static ERL_NIF_TERM get_resource(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { - ADD_CALL("get_priv_data_ptr"); - return enif_make_ulong(env, (unsigned long)enif_get_data(env)); + NifModPrivData* data = (NifModPrivData*) enif_priv_data(env); + ErlNifBinary obin; + unsigned ix; + void* a; + if (!enif_get_uint(env, argv[0], &ix) || ix >= RT_MAX + || !enif_get_resource(env, argv[1], data->rt_arr[ix], &a) + || !enif_alloc_binary(env, enif_sizeof_resource(env, a), &obin)) { + return enif_make_badarg(env); + } + memcpy(obin.data, a, obin.size); + return enif_make_binary(env, &obin); } - static ErlNifFunc nif_funcs[] = { {"lib_version", 0, lib_version}, - {"call_history", 0, call_history}, - {"get_priv_data_ptr", 0, get_priv_data_ptr} + {"get_priv_data_ptr", 0, get_priv_data_ptr}, + {"make_new_resource", 2, make_new_resource}, + {"get_resource", 2, get_resource} }; #if NIF_LIB_VER != 3 diff --git a/erts/emulator/test/nif_SUITE_data/nif_mod.erl b/erts/emulator/test/nif_SUITE_data/nif_mod.erl index 93da6590a0..7888a589e7 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_mod.erl +++ b/erts/emulator/test/nif_SUITE_data/nif_mod.erl @@ -21,15 +21,19 @@ -include("test_server.hrl"). --export([load_nif_lib/2, start/0, lib_version/0, call_history/0, get_priv_data_ptr/0]). +-export([load_nif_lib/2, load_nif_lib/3, start/0, lib_version/0, call_history/0, + get_priv_data_ptr/0, make_new_resource/2, get_resource/2]). -export([loop/0, upgrade/1]). -define(nif_stub,nif_stub_error(?LINE)). load_nif_lib(Config, Ver) -> + load_nif_lib(Config, Ver, []). + +load_nif_lib(Config, Ver, LoadInfo) -> ?line Path = ?config(data_dir, Config), - erlang:load_nif(filename:join(Path,libname(Ver)), 0). + erlang:load_nif(filename:join(Path,libname(Ver)), LoadInfo). libname(no_init) -> libname(3); libname(Ver) when is_integer(Ver) -> @@ -59,6 +63,8 @@ lib_version() -> % NIF call_history() -> ?nif_stub. get_priv_data_ptr() -> ?nif_stub. +make_new_resource(_,_) -> ?nif_stub. +get_resource(_,_) -> ?nif_stub. nif_stub_error(Line) -> exit({nif_not_loaded,module,?MODULE,line,Line}). diff --git a/erts/emulator/test/nif_SUITE_data/nif_mod.h b/erts/emulator/test/nif_SUITE_data/nif_mod.h index 2dfdc75176..0eaf91d6e1 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_mod.h +++ b/erts/emulator/test/nif_SUITE_data/nif_mod.h @@ -4,14 +4,19 @@ typedef struct call_info_t unsigned lib_ver; int static_cntA; int static_cntB; + char* arg; + int arg_sz; char func_name[1]; /* must be last */ }CallInfo; +#define RT_MAX 5 typedef struct { + ErlNifMutex* mtx; int calls; int ref_cnt; CallInfo* call_history; + ErlNifResourceType* rt_arr[RT_MAX]; }NifModPrivData; diff --git a/erts/emulator/test/nif_SUITE_data/tail.txt b/erts/emulator/test/nif_SUITE_data/tail.txt new file mode 100644 index 0000000000..7f06c118c1 --- /dev/null +++ b/erts/emulator/test/nif_SUITE_data/tail.txt @@ -0,0 +1,5 @@ + + +#include "tester.c" /* poor mans linker */ + + diff --git a/erts/emulator/test/nif_SUITE_data/testcase_driver.h b/erts/emulator/test/nif_SUITE_data/testcase_driver.h new file mode 100644 index 0000000000..98339e4746 --- /dev/null +++ b/erts/emulator/test/nif_SUITE_data/testcase_driver.h @@ -0,0 +1,59 @@ +/* ``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 via the world wide web 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. + * + * The Initial Developer of the Original Code is Ericsson Utvecklings AB. + * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings + * AB. All Rights Reserved.'' + * + * $Id$ + */ + +#ifndef TESTCASE_DRIVER_H__ +#define TESTCASE_DRIVER_H__ + +#include "erl_nif.h" +#include +#include + +typedef struct { + char *testcase_name; + char *command; + int command_len; + void *extra; +} TestCaseState_t; + +#define ASSERT_CLNUP(TCS, B, CLN) \ +do { \ + if (!(B)) { \ + CLN; \ + testcase_assertion_failed((TCS), __FILE__, __LINE__, #B); \ + } \ +} while (0) + +#define ASSERT(TCS, B) ASSERT_CLNUP(TCS, B, (void) 0) + + +void testcase_printf(TestCaseState_t *tcs, char *frmt, ...); +void testcase_succeeded(TestCaseState_t *tcs, char *frmt, ...); +void testcase_skipped(TestCaseState_t *tcs, char *frmt, ...); +void testcase_failed(TestCaseState_t *tcs, char *frmt, ...); +int testcase_assertion_failed(TestCaseState_t *tcs, char *file, int line, + char *assertion); +void *testcase_alloc(size_t size); +void *testcase_realloc(void *ptr, size_t size); +void testcase_free(void *ptr); + + +char *testcase_name(void); +void testcase_run(TestCaseState_t *tcs); +void testcase_cleanup(TestCaseState_t *tcs); + +#endif diff --git a/erts/emulator/test/nif_SUITE_data/tester.c b/erts/emulator/test/nif_SUITE_data/tester.c new file mode 100644 index 0000000000..08466d0f18 --- /dev/null +++ b/erts/emulator/test/nif_SUITE_data/tester.c @@ -0,0 +1,73 @@ +#include "erl_nif.h" + +#include +#include + +void testcase_printf(TestCaseState_t *tcs, char *frmt, ...) +{ + va_list va; + va_start(va, frmt); + vfprintf(stderr, frmt, va); + va_end(va); + fprintf(stderr, "\r"); +} + +void testcase_succeeded(TestCaseState_t *tcs, char *frmt, ...) +{ +} + +void testcase_skipped(TestCaseState_t *tcs, char *frmt, ...) +{ +} + +void testcase_failed(TestCaseState_t *tcs, char *frmt, ...) +{ + va_list va; + va_start(va, frmt); + vfprintf(stderr, frmt, va); + va_end(va); + abort(); +} + +int testcase_assertion_failed(TestCaseState_t *tcs, char *file, int line, + char *assertion) +{ + testcase_failed(tcs, "ASSERTION '%s' FAILED at %s:%d\r\n", + assertion, file, line); + abort(); + return 0; /*?*/ +} + +void *testcase_alloc(size_t size) +{ + return malloc(size); +} +void *testcase_realloc(void *ptr, size_t size) +{ + return realloc(ptr, size); +} +void testcase_free(void *ptr) +{ + free(ptr); +} + +void testcase_run(TestCaseState_t *tcs); + +static int reload(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) +{ + return 0; +} + +static ERL_NIF_TERM run(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + testcase_run(NULL); + return enif_make_atom(env, "ok"); +} + +static ErlNifFunc nif_funcs[] = +{ + {"run", 0, run} +}; + +ERL_NIF_INIT(tester,nif_funcs,NULL,reload,NULL,NULL) + diff --git a/erts/emulator/test/nif_SUITE_data/tester.erl b/erts/emulator/test/nif_SUITE_data/tester.erl new file mode 100644 index 0000000000..9df2158200 --- /dev/null +++ b/erts/emulator/test/nif_SUITE_data/tester.erl @@ -0,0 +1,13 @@ +-module(tester). + +-include("test_server.hrl"). + +-export([load_nif_lib/2, run/0]). + + +load_nif_lib(Config, LibName) -> + ?line Path = ?config(data_dir, Config), + erlang:load_nif(filename:join(Path,LibName), []). + +run() -> + exit({nif_not_loaded,?MODULE,?LINE}). -- cgit v1.2.3