diff options
Diffstat (limited to 'lib/stdlib/test/fixtable_SUITE.erl')
-rw-r--r-- | lib/stdlib/test/fixtable_SUITE.erl | 414 |
1 files changed, 414 insertions, 0 deletions
diff --git a/lib/stdlib/test/fixtable_SUITE.erl b/lib/stdlib/test/fixtable_SUITE.erl new file mode 100644 index 0000000000..9f21308ad4 --- /dev/null +++ b/lib/stdlib/test/fixtable_SUITE.erl @@ -0,0 +1,414 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1999-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% +%% +%%%---------------------------------------------------------------------- +%%% Purpose : Tests the safe_fixtable functions in both ets and dets. +%%%---------------------------------------------------------------------- + +-module(fixtable_SUITE). +-export([all/1]). +%%% Test cases +-export([multiple_fixes/1, multiple_processes/1, + other_process_deletes/1, owner_dies/1, + other_process_closes/1,insert_same_key/1]). +-export([fixbag/1]). +-export([init_per_testcase/2, fin_per_testcase/2]). +%%% Internal exports +-export([command_loop/0,start_commander/0]). + +all(suite) -> {req, [stdlib], + [multiple_fixes, multiple_processes, + other_process_deletes, owner_dies, + other_process_closes,insert_same_key,fixbag]}. + +-include("test_server.hrl"). + +%%% I wrote this thinking I would use more than one temporary at a time, but +%%% I wasn't... Well, maybe in the future... +-define(DETS_TEMPORARIES, [tmp1]). +-define(ETS_TEMPORARIES, [gurksmetsmedaljong]). +-define(DETS_TMP1,hd(?DETS_TEMPORARIES)). +-define(ETS_TMP1,hd(?ETS_TEMPORARIES)). + +-define(HELPER_NODE, (atom_to_list(?MODULE) ++ "_helper1")). + +init_per_testcase(_Func, Config) -> + PrivDir = ?config(priv_dir,Config), + file:make_dir(PrivDir), + Dog=test_server:timetrap(test_server:seconds(60)), + [{watchdog, Dog}|Config]. + +fin_per_testcase(_Func, Config) -> + Dog=?config(watchdog, Config), + test_server:timetrap_cancel(Dog), + lists:foreach(fun(X) -> + (catch dets:close(X)), + (catch file:delete(dets_filename(X,Config))) + end, + ?DETS_TEMPORARIES), + lists:foreach(fun(X) -> + (catch ets:delete(X)) + end, + ?ETS_TEMPORARIES). + + +-ifdef(DEBUG). +-define(LOG(X), show(X,?LINE)). + +show(Term, Line) -> + io:format("~p: ~p~n", [Line,Term]), + Term. +-else. +-define(LOG(X),X). +-endif. + + +fixbag(doc) -> + ["Check for bug OTP-5087, safe_fixtable for bags could give " + "incorrect lookups"]; +fixbag(suite) -> + []; +fixbag(Config) when list(Config) -> + ?line T = ets:new(x,[bag]), + ?line ets:insert(T,{a,1}), + ?line ets:insert(T,{a,2}), + ?line ets:safe_fixtable(T,true), + ?line ets:match_delete(T,{a,2}), + ?line ets:insert(T,{a,3}), + ?line Res = ets:lookup(T,a), + ?line ets:safe_fixtable(T,false), + ?line Res = ets:lookup(T,a), + ok. + + + +insert_same_key(doc) -> + ["Check correct behaviour if a key is deleted and reinserted during fixation."]; +insert_same_key(suite) -> + []; +insert_same_key(Config) when list(Config) -> + ?line {ok,Dets1} = dets:open_file(?DETS_TMP1, + [{file, dets_filename(?DETS_TMP1,Config)}]), + ?line Ets1 = ets:new(ets,[]), + ?line insert_same_key(Dets1,dets,Config), + ?line insert_same_key(Ets1,ets,Config), + ?line ets:insert(Ets1,{1,2}), + ?line 1 = ets:info(Ets1,size), + ?line dets:insert(Dets1,{1,2}), + ?line 1 = dets:info(Dets1,size), + ?line dets:close(Dets1), + ?line (catch file:delete(dets_filename(Dets1,Config))), + ?line ets:delete(Ets1), + ?line {ok,Dets2} = dets:open_file(?DETS_TMP1, + [{type,bag},{file, dets_filename(?DETS_TMP1,Config)}]), + ?line Ets2 = ets:new(ets,[bag]), + ?line insert_same_key(Dets2,dets,Config), + ?line insert_same_key(Ets2,ets,Config), + ?line ets:insert(Ets2,{1,2}), + ?line 2 = ets:info(Ets2,size), + ?line ets:insert(Ets2,{1,2}), + ?line 2 = ets:info(Ets2,size), + ?line dets:insert(Dets2,{1,2}), + ?line 2 = dets:info(Dets2,size), + ?line dets:insert(Dets2,{1,2}), + ?line 2 = dets:info(Dets2,size), + ?line dets:close(Dets2), + ?line (catch file:delete(dets_filename(Dets2,Config))), + ?line ets:delete(Ets2), + ?line {ok,Dets3} = dets:open_file(?DETS_TMP1, + [{type,duplicate_bag}, + {file, dets_filename(?DETS_TMP1,Config)}]), + ?line Ets3 = ets:new(ets,[duplicate_bag]), + ?line insert_same_key(Dets3,dets,Config), + ?line insert_same_key(Ets3,ets,Config), + ?line ets:insert(Ets3,{1,2}), + ?line 2 = ets:info(Ets3,size), + ?line ets:insert(Ets3,{1,2}), + ?line 3 = ets:info(Ets3,size), + ?line dets:insert(Dets3,{1,2}), + ?line 2 = dets:info(Dets3,size), + ?line dets:insert(Dets3,{1,2}), + ?line 3 = dets:info(Dets3,size), + ?line dets:close(Dets3), + ?line (catch file:delete(dets_filename(Dets3,Config))), + ?line ets:delete(Ets3), + ok. + +insert_same_key(Tab,Mod,_Config) -> + ?line Mod:insert(Tab,{1,1}), + ?line Mod:insert(Tab,{1,2}), + ?line Mod:insert(Tab,{2,2}), + ?line Mod:insert(Tab,{2,2}), + ?line Mod:safe_fixtable(Tab,true), + ?line Mod:delete(Tab,1), + ?line Mod:insert(Tab,{1,1}), + ?line Expect = case Mod:info(Tab,type) of + bag -> + Mod:insert(Tab,{1,2}), + 2; + _ -> + 1 + end, + ?line Mod:delete(Tab,2), + ?line Mod:safe_fixtable(Tab,false), + ?line case Mod:info(Tab,size) of + Expect -> + ok; + _ -> + exit({size_field_wrong,{Mod,Mod:info(Tab)}}) + end. + + + + +owner_dies(doc) -> + ["Check correct behaviour if the table owner dies."]; +owner_dies(suite) -> + []; +owner_dies(Config) when list(Config) -> + ?line P1 = start_commander(), + ?line Ets1 = command(P1,{ets,new,[ets,[]]}), + ?line command(P1,{ets,safe_fixtable,[Ets1,true]}), + ?line {_,[{P1,1}]} = ets:info(Ets1, safe_fixed), + ?line stop_commander(P1), + ?line undefined = ets:info(Ets1, safe_fixed), + ?line P2 = start_commander(), + ?line Ets2 = command(P2,{ets,new,[ets,[public]]}), + ?line command(P2,{ets,safe_fixtable,[Ets2,true]}), + ?line ets:safe_fixtable(Ets2,true), + ?line true = ets:info(Ets2, fixed), + ?line {_,[{_,1},{_,1}]} = ets:info(Ets2, safe_fixed), + ?line stop_commander(P2), + ?line undefined = ets:info(Ets2, safe_fixed), + ?line undefined = ets:info(Ets2, fixed), + ?line P3 = start_commander(), + ?line {ok,Dets} = ?LOG(command(P3, {dets, open_file, + [?DETS_TMP1, + [{file, + dets_filename(?DETS_TMP1, + Config)}]]})), + ?line command(P3, {dets, safe_fixtable, [Dets, true]}), + ?line {_,[{P3,1}]} = dets:info(Dets, safe_fixed), + ?line true = dets:info(Dets, fixed), + ?line stop_commander(P3), + ?line undefined = dets:info(Dets, safe_fixed), + ?line undefined = dets:info(Dets, fixed), + ?line P4 = start_commander(), + ?line {ok,Dets} = command(P4, {dets, open_file, + [?DETS_TMP1, + [{file, dets_filename(?DETS_TMP1,Config)}]]}), + ?line {ok,Dets} = dets:open_file(?DETS_TMP1, + [{file, dets_filename(?DETS_TMP1,Config)}]), + ?line false = dets:info(Dets, safe_fixed), + ?line command(P4, {dets, safe_fixtable, [Dets, true]}), + ?line dets:safe_fixtable(Dets, true), + ?line {_,[{_,1},{_,1}]} = dets:info(Dets, safe_fixed), + ?line dets:safe_fixtable(Dets, true), + ?line stop_commander(P4), + ?line S = self(), + ?line {_,[{S,2}]} = dets:info(Dets, safe_fixed), + ?line true = dets:info(Dets, fixed), + ?line dets:close(Dets), + ?line undefined = dets:info(Dets, fixed), + ?line undefined = dets:info(Dets, safe_fixed), + ok. + + +other_process_closes(doc) -> + ["When another process closes an dets table, different " + "things should happen depending on if it has opened it before."]; + +other_process_closes(suite) -> + []; + +other_process_closes(Config) when list(Config) -> + ?line {ok,Dets} = dets:open_file(?DETS_TMP1, + [{file, dets_filename(tmp1,Config)}]), + ?line P2 = start_commander(), + ?line dets:safe_fixtable(Dets,true), + ?line S = self(), + ?line {_,[{S,1}]} = dets:info(Dets, safe_fixed), + ?line command(P2,{dets, safe_fixtable, [Dets, true]}), + ?line {_,[_,_]} = dets:info(Dets, safe_fixed), + ?line {error, not_owner} = command(P2,{dets, close, [Dets]}), + ?line {_,[_,_]} = dets:info(Dets, safe_fixed), + ?line command(P2,{dets, open_file,[?DETS_TMP1, + [{file, + dets_filename(?DETS_TMP1, Config)}]]}), + ?line {_,[_,_]} = dets:info(Dets, safe_fixed), + ?line command(P2,{dets, close, [Dets]}), + ?line stop_commander(P2), + ?line {_,[{S,1}]} = dets:info(Dets, safe_fixed), + ?line true = dets:info(Dets,fixed), + ?line dets:close(Dets), + ?line undefined = dets:info(Dets,fixed), + ?line undefined = dets:info(Dets, safe_fixed), + ok. + +other_process_deletes(doc) -> + ["Check that fixtable structures are cleaned up if another process " + "deletes an ets table"]; +other_process_deletes(suite) -> + []; +other_process_deletes(Config) when list(Config) -> + ?line Ets = ets:new(ets,[public]), + ?line P = start_commander(), + ?line ets:safe_fixtable(Ets,true), + ?line ets:safe_fixtable(Ets,true), + ?line true = ets:info(Ets, fixed), + ?line {_,_} = ets:info(Ets, safe_fixed), + ?line command(P,{ets,delete,[Ets]}), + ?line stop_commander(P), + ?line undefined = ets:info(Ets, fixed), + ?line undefined = ets:info(Ets, safe_fixed), + ok. + +multiple_fixes(doc) -> + ["Check that multiple safe_fixtable keeps the reference counter."]; +multiple_fixes(suite) -> + []; +multiple_fixes(Config) when list(Config) -> + ?line {ok,Dets} = dets:open_file(?DETS_TMP1, + [{file, dets_filename(?DETS_TMP1,Config)}]), + ?line Ets = ets:new(ets,[]), + ?line multiple_fixes(Dets,dets), + ?line multiple_fixes(Ets,ets), + ?line dets:close(Dets), + ok. + +multiple_fixes(Tab, Mod) -> + ?line false = Mod:info(Tab,fixed), + ?line false = Mod:info(Tab, safe_fixed), + ?line Mod:safe_fixtable(Tab, true), + ?line true = Mod:info(Tab,fixed), + ?line S = self(), + ?line {_,[{S,1}]} = Mod:info(Tab, safe_fixed), + ?line Mod:safe_fixtable(Tab, true), + ?line Mod:safe_fixtable(Tab, true), + ?line {_,[{S,3}]} = Mod:info(Tab, safe_fixed), + ?line true = Mod:info(Tab,fixed), + ?line Mod:safe_fixtable(Tab, false), + ?line {_,[{S,2}]} = Mod:info(Tab, safe_fixed), + ?line true = Mod:info(Tab,fixed), + ?line Mod:safe_fixtable(Tab, false), + ?line {_,[{S,1}]} = Mod:info(Tab, safe_fixed), + ?line true = Mod:info(Tab,fixed), + ?line Mod:safe_fixtable(Tab, false), + ?line false = Mod:info(Tab, safe_fixed), + ?line false = Mod:info(Tab,fixed). + +multiple_processes(doc) -> + ["Check that multiple safe_fixtable across processes are reference " + "counted OK"]; +multiple_processes(suite) -> + []; +multiple_processes(Config) when list(Config) -> + ?line {ok,Dets} = dets:open_file(?DETS_TMP1,[{file, + dets_filename(?DETS_TMP1, + Config)}]), + ?line Ets = ets:new(ets,[public]), + ?line multiple_processes(Dets,dets), + ?line multiple_processes(Ets,ets), + ok. + +multiple_processes(Tab, Mod) -> + ?line io:format("Mod = ~p\n", [Mod]), + ?line P1 = start_commander(), + ?line P2 = start_commander(), + ?line false = Mod:info(Tab,fixed), + ?line false = Mod:info(Tab, safe_fixed), + ?line command(P1, {Mod, safe_fixtable, [Tab,true]}), + ?line true = Mod:info(Tab,fixed), + ?line {_,[{P1,1}]} = Mod:info(Tab, safe_fixed), + ?line command(P2, {Mod, safe_fixtable, [Tab,true]}), + ?line true = Mod:info(Tab,fixed), + ?line {_,L} = Mod:info(Tab,safe_fixed), + ?line true = (lists:sort(L) == lists:sort([{P1,1},{P2,1}])), + ?line command(P2, {Mod, safe_fixtable, [Tab,true]}), + ?line {_,L2} = Mod:info(Tab,safe_fixed), + ?line true = (lists:sort(L2) == lists:sort([{P1,1},{P2,2}])), + ?line command(P2, {Mod, safe_fixtable, [Tab,false]}), + ?line true = Mod:info(Tab,fixed), + ?line {_,L3} = Mod:info(Tab,safe_fixed), + ?line true = (lists:sort(L3) == lists:sort([{P1,1},{P2,1}])), + ?line command(P2, {Mod, safe_fixtable, [Tab,false]}), + ?line true = Mod:info(Tab,fixed), + ?line {_,[{P1,1}]} = Mod:info(Tab, safe_fixed), + ?line stop_commander(P1), + ?line receive after 1000 -> ok end, + ?line false = Mod:info(Tab,fixed), + ?line false = Mod:info(Tab, safe_fixed), + ?line command(P2, {Mod, safe_fixtable, [Tab,true]}), + ?line true = Mod:info(Tab,fixed), + ?line {_,[{P2,1}]} = Mod:info(Tab, safe_fixed), + case Mod of + dets -> + ?line dets:close(Tab); + ets -> + ?line ets:delete(Tab) + end, + ?line stop_commander(P2), + ?line receive after 1000 -> ok end, + ?line undefined = Mod:info(Tab, safe_fixed), + ok. + + + +%%% Helpers +dets_filename(Base, Config) when atom(Base) -> + dets_filename(atom_to_list(Base) ++ ".dat", Config); +dets_filename(Basename, Config) -> + PrivDir = ?config(priv_dir,Config), + filename:join(PrivDir, Basename). + +command_loop() -> + receive + {From, command, {M,F,A}} -> + Res = (catch apply(M, F, A)), + From ! {self(), Res}, + command_loop(); + die -> + ok + end. + +start_commander() -> + spawn(?MODULE, command_loop, []). + +stop_commander(Pid) -> + process_flag(trap_exit, true), + link(Pid), + Pid ! die, + receive + {'EXIT',Pid,_} -> + timer:sleep(1), % let other processes handle the signal as well + true + after 5000 -> + exit(stop_timeout) + end. + +command(Pid,MFA) -> + Pid ! {self(), command, MFA}, + receive + {Pid, Res} -> + Res + after 20000 -> + exit(command_timeout) + end. + + + |