1: %% 2: %% %CopyrightBegin% 3: %% 4: %% Copyright Ericsson AB 1999-2011. All Rights Reserved. 5: %% 6: %% The contents of this file are subject to the Erlang Public License, 7: %% Version 1.1, (the "License"); you may not use this file except in 8: %% compliance with the License. You should have received a copy of the 9: %% Erlang Public License along with this software. If not, it can be 10: %% retrieved online at http://www.erlang.org/. 11: %% 12: %% Software distributed under the License is distributed on an "AS IS" 13: %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 14: %% the License for the specific language governing rights and limitations 15: %% under the License. 16: %% 17: %% %CopyrightEnd% 18: %% 19: %%%---------------------------------------------------------------------- 20: %%% Purpose : Tests the safe_fixtable functions in both ets and dets. 21: %%%---------------------------------------------------------------------- 22: 23: -module(fixtable_SUITE). 24: -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, 25: init_per_group/2,end_per_group/2]). 26: %%% Test cases 27: -export([multiple_fixes/1, multiple_processes/1, 28: other_process_deletes/1, owner_dies/1, 29: other_process_closes/1,insert_same_key/1]). 30: -export([fixbag/1]). 31: -export([init_per_testcase/2, end_per_testcase/2]). 32: %%% Internal exports 33: -export([command_loop/0,start_commander/0]). 34: 35: suite() -> [{ct_hooks,[ts_install_cth]}]. 36: 37: all() -> 38: [multiple_fixes, multiple_processes, 39: other_process_deletes, owner_dies, other_process_closes, 40: insert_same_key, fixbag]. 41: 42: groups() -> 43: []. 44: 45: init_per_suite(Config) -> 46: Config. 47: 48: end_per_suite(_Config) -> 49: ok. 50: 51: init_per_group(_GroupName, Config) -> 52: Config. 53: 54: end_per_group(_GroupName, Config) -> 55: Config. 56: 57: 58: -include_lib("test_server/include/test_server.hrl"). 59: 60: %%% I wrote this thinking I would use more than one temporary at a time, but 61: %%% I wasn't... Well, maybe in the future... 62: -define(DETS_TEMPORARIES, [tmp1]). 63: -define(ETS_TEMPORARIES, [gurksmetsmedaljong]). 64: -define(DETS_TMP1,hd(?DETS_TEMPORARIES)). 65: -define(ETS_TMP1,hd(?ETS_TEMPORARIES)). 66: 67: -define(HELPER_NODE, (atom_to_list(?MODULE) ++ "_helper1")). 68: 69: init_per_testcase(_Func, Config) -> 70: PrivDir = ?config(priv_dir,Config), 71: file:make_dir(PrivDir), 72: Dog=test_server:timetrap(test_server:seconds(60)), 73: [{watchdog, Dog}|Config]. 74: 75: end_per_testcase(_Func, Config) -> 76: Dog=?config(watchdog, Config), 77: test_server:timetrap_cancel(Dog), 78: lists:foreach(fun(X) -> 79: (catch dets:close(X)), 80: (catch file:delete(dets_filename(X,Config))) 81: end, 82: ?DETS_TEMPORARIES), 83: lists:foreach(fun(X) -> 84: (catch ets:delete(X)) 85: end, 86: ?ETS_TEMPORARIES). 87: 88: 89: -ifdef(DEBUG). 90: -define(LOG(X), show(X,?LINE)). 91: 92: show(Term, Line) -> 93: io:format("~p: ~p~n", [Line,Term]), 94: Term. 95: -else. 96: -define(LOG(X),X). 97: -endif. 98: 99: 100: fixbag(doc) -> 101: ["Check for bug OTP-5087, safe_fixtable for bags could give " 102: "incorrect lookups"]; 103: fixbag(suite) -> 104: []; 105: fixbag(Config) when is_list(Config) -> 106: ?line T = ets:new(x,[bag]), 107: ?line ets:insert(T,{a,1}), 108: ?line ets:insert(T,{a,2}), 109: ?line ets:safe_fixtable(T,true), 110: ?line ets:match_delete(T,{a,2}), 111: ?line ets:insert(T,{a,3}), 112: ?line Res = ets:lookup(T,a), 113: ?line ets:safe_fixtable(T,false), 114: ?line Res = ets:lookup(T,a), 115: ok. 116: 117: 118: 119: insert_same_key(doc) -> 120: ["Check correct behaviour if a key is deleted and reinserted during fixation."]; 121: insert_same_key(suite) -> 122: []; 123: insert_same_key(Config) when is_list(Config) -> 124: ?line {ok,Dets1} = dets:open_file(?DETS_TMP1, 125: [{file, dets_filename(?DETS_TMP1,Config)}]), 126: ?line Ets1 = ets:new(ets,[]), 127: ?line insert_same_key(Dets1,dets,Config), 128: ?line insert_same_key(Ets1,ets,Config), 129: ?line ets:insert(Ets1,{1,2}), 130: ?line 1 = ets:info(Ets1,size), 131: ?line dets:insert(Dets1,{1,2}), 132: ?line 1 = dets:info(Dets1,size), 133: ?line dets:close(Dets1), 134: ?line (catch file:delete(dets_filename(Dets1,Config))), 135: ?line ets:delete(Ets1), 136: ?line {ok,Dets2} = dets:open_file(?DETS_TMP1, 137: [{type,bag},{file, dets_filename(?DETS_TMP1,Config)}]), 138: ?line Ets2 = ets:new(ets,[bag]), 139: ?line insert_same_key(Dets2,dets,Config), 140: ?line insert_same_key(Ets2,ets,Config), 141: ?line ets:insert(Ets2,{1,2}), 142: ?line 2 = ets:info(Ets2,size), 143: ?line ets:insert(Ets2,{1,2}), 144: ?line 2 = ets:info(Ets2,size), 145: ?line dets:insert(Dets2,{1,2}), 146: ?line 2 = dets:info(Dets2,size), 147: ?line dets:insert(Dets2,{1,2}), 148: ?line 2 = dets:info(Dets2,size), 149: ?line dets:close(Dets2), 150: ?line (catch file:delete(dets_filename(Dets2,Config))), 151: ?line ets:delete(Ets2), 152: ?line {ok,Dets3} = dets:open_file(?DETS_TMP1, 153: [{type,duplicate_bag}, 154: {file, dets_filename(?DETS_TMP1,Config)}]), 155: ?line Ets3 = ets:new(ets,[duplicate_bag]), 156: ?line insert_same_key(Dets3,dets,Config), 157: ?line insert_same_key(Ets3,ets,Config), 158: ?line ets:insert(Ets3,{1,2}), 159: ?line 2 = ets:info(Ets3,size), 160: ?line ets:insert(Ets3,{1,2}), 161: ?line 3 = ets:info(Ets3,size), 162: ?line dets:insert(Dets3,{1,2}), 163: ?line 2 = dets:info(Dets3,size), 164: ?line dets:insert(Dets3,{1,2}), 165: ?line 3 = dets:info(Dets3,size), 166: ?line dets:close(Dets3), 167: ?line (catch file:delete(dets_filename(Dets3,Config))), 168: ?line ets:delete(Ets3), 169: ok. 170: 171: insert_same_key(Tab,Mod,_Config) -> 172: ?line Mod:insert(Tab,{1,1}), 173: ?line Mod:insert(Tab,{1,2}), 174: ?line Mod:insert(Tab,{2,2}), 175: ?line Mod:insert(Tab,{2,2}), 176: ?line Mod:safe_fixtable(Tab,true), 177: ?line Mod:delete(Tab,1), 178: ?line Mod:insert(Tab,{1,1}), 179: ?line Expect = case Mod:info(Tab,type) of 180: bag -> 181: Mod:insert(Tab,{1,2}), 182: 2; 183: _ -> 184: 1 185: end, 186: ?line Mod:delete(Tab,2), 187: ?line Mod:safe_fixtable(Tab,false), 188: ?line case Mod:info(Tab,size) of 189: Expect -> 190: ok; 191: _ -> 192: exit({size_field_wrong,{Mod,Mod:info(Tab)}}) 193: end. 194: 195: 196: 197: 198: owner_dies(doc) -> 199: ["Check correct behaviour if the table owner dies."]; 200: owner_dies(suite) -> 201: []; 202: owner_dies(Config) when is_list(Config) -> 203: ?line P1 = start_commander(), 204: ?line Ets1 = command(P1,{ets,new,[ets,[]]}), 205: ?line command(P1,{ets,safe_fixtable,[Ets1,true]}), 206: ?line {_,[{P1,1}]} = ets:info(Ets1, safe_fixed), 207: ?line stop_commander(P1), 208: ?line undefined = ets:info(Ets1, safe_fixed), 209: ?line P2 = start_commander(), 210: ?line Ets2 = command(P2,{ets,new,[ets,[public]]}), 211: ?line command(P2,{ets,safe_fixtable,[Ets2,true]}), 212: ?line ets:safe_fixtable(Ets2,true), 213: ?line true = ets:info(Ets2, fixed), 214: ?line {_,[{_,1},{_,1}]} = ets:info(Ets2, safe_fixed), 215: ?line stop_commander(P2), 216: ?line undefined = ets:info(Ets2, safe_fixed), 217: ?line undefined = ets:info(Ets2, fixed), 218: ?line P3 = start_commander(), 219: ?line {ok,Dets} = ?LOG(command(P3, {dets, open_file, 220: [?DETS_TMP1, 221: [{file, 222: dets_filename(?DETS_TMP1, 223: Config)}]]})), 224: ?line command(P3, {dets, safe_fixtable, [Dets, true]}), 225: ?line {_,[{P3,1}]} = dets:info(Dets, safe_fixed), 226: ?line true = dets:info(Dets, fixed), 227: ?line stop_commander(P3), 228: ?line undefined = dets:info(Dets, safe_fixed), 229: ?line undefined = dets:info(Dets, fixed), 230: ?line P4 = start_commander(), 231: ?line {ok,Dets} = command(P4, {dets, open_file, 232: [?DETS_TMP1, 233: [{file, dets_filename(?DETS_TMP1,Config)}]]}), 234: ?line {ok,Dets} = dets:open_file(?DETS_TMP1, 235: [{file, dets_filename(?DETS_TMP1,Config)}]), 236: ?line false = dets:info(Dets, safe_fixed), 237: ?line command(P4, {dets, safe_fixtable, [Dets, true]}), 238: ?line dets:safe_fixtable(Dets, true), 239: ?line {_,[{_,1},{_,1}]} = dets:info(Dets, safe_fixed), 240: ?line dets:safe_fixtable(Dets, true), 241: ?line stop_commander(P4), 242: ?line S = self(), 243: ?line {_,[{S,2}]} = dets:info(Dets, safe_fixed), 244: ?line true = dets:info(Dets, fixed), 245: ?line dets:close(Dets), 246: ?line undefined = dets:info(Dets, fixed), 247: ?line undefined = dets:info(Dets, safe_fixed), 248: ok. 249: 250: 251: other_process_closes(doc) -> 252: ["When another process closes an dets table, different " 253: "things should happen depending on if it has opened it before."]; 254: 255: other_process_closes(suite) -> 256: []; 257: 258: other_process_closes(Config) when is_list(Config) -> 259: ?line {ok,Dets} = dets:open_file(?DETS_TMP1, 260: [{file, dets_filename(tmp1,Config)}]), 261: ?line P2 = start_commander(), 262: ?line dets:safe_fixtable(Dets,true), 263: ?line S = self(), 264: ?line {_,[{S,1}]} = dets:info(Dets, safe_fixed), 265: ?line command(P2,{dets, safe_fixtable, [Dets, true]}), 266: ?line {_,[_,_]} = dets:info(Dets, safe_fixed), 267: ?line {error, not_owner} = command(P2,{dets, close, [Dets]}), 268: ?line {_,[_,_]} = dets:info(Dets, safe_fixed), 269: ?line command(P2,{dets, open_file,[?DETS_TMP1, 270: [{file, 271: dets_filename(?DETS_TMP1, Config)}]]}), 272: ?line {_,[_,_]} = dets:info(Dets, safe_fixed), 273: ?line command(P2,{dets, close, [Dets]}), 274: ?line stop_commander(P2), 275: ?line {_,[{S,1}]} = dets:info(Dets, safe_fixed), 276: ?line true = dets:info(Dets,fixed), 277: ?line dets:close(Dets), 278: ?line undefined = dets:info(Dets,fixed), 279: ?line undefined = dets:info(Dets, safe_fixed), 280: ok. 281: 282: other_process_deletes(doc) -> 283: ["Check that fixtable structures are cleaned up if another process " 284: "deletes an ets table"]; 285: other_process_deletes(suite) -> 286: []; 287: other_process_deletes(Config) when is_list(Config) -> 288: ?line Ets = ets:new(ets,[public]), 289: ?line P = start_commander(), 290: ?line ets:safe_fixtable(Ets,true), 291: ?line ets:safe_fixtable(Ets,true), 292: ?line true = ets:info(Ets, fixed), 293: ?line {_,_} = ets:info(Ets, safe_fixed), 294: ?line command(P,{ets,delete,[Ets]}), 295: ?line stop_commander(P), 296: ?line undefined = ets:info(Ets, fixed), 297: ?line undefined = ets:info(Ets, safe_fixed), 298: ok. 299: 300: multiple_fixes(doc) -> 301: ["Check that multiple safe_fixtable keeps the reference counter."]; 302: multiple_fixes(suite) -> 303: []; 304: multiple_fixes(Config) when is_list(Config) -> 305: ?line {ok,Dets} = dets:open_file(?DETS_TMP1, 306: [{file, dets_filename(?DETS_TMP1,Config)}]), 307: ?line Ets = ets:new(ets,[]), 308: ?line multiple_fixes(Dets,dets), 309: ?line multiple_fixes(Ets,ets), 310: ?line dets:close(Dets), 311: ok. 312: 313: multiple_fixes(Tab, Mod) -> 314: ?line false = Mod:info(Tab,fixed), 315: ?line false = Mod:info(Tab, safe_fixed), 316: ?line Mod:safe_fixtable(Tab, true), 317: ?line true = Mod:info(Tab,fixed), 318: ?line S = self(), 319: ?line {_,[{S,1}]} = Mod:info(Tab, safe_fixed), 320: ?line Mod:safe_fixtable(Tab, true), 321: ?line Mod:safe_fixtable(Tab, true), 322: ?line {_,[{S,3}]} = Mod:info(Tab, safe_fixed), 323: ?line true = Mod:info(Tab,fixed), 324: ?line Mod:safe_fixtable(Tab, false), 325: ?line {_,[{S,2}]} = Mod:info(Tab, safe_fixed), 326: ?line true = Mod:info(Tab,fixed), 327: ?line Mod:safe_fixtable(Tab, false), 328: ?line {_,[{S,1}]} = Mod:info(Tab, safe_fixed), 329: ?line true = Mod:info(Tab,fixed), 330: ?line Mod:safe_fixtable(Tab, false), 331: ?line false = Mod:info(Tab, safe_fixed), 332: ?line false = Mod:info(Tab,fixed). 333: 334: multiple_processes(doc) -> 335: ["Check that multiple safe_fixtable across processes are reference " 336: "counted OK"]; 337: multiple_processes(suite) -> 338: []; 339: multiple_processes(Config) when is_list(Config) -> 340: ?line {ok,Dets} = dets:open_file(?DETS_TMP1,[{file, 341: dets_filename(?DETS_TMP1, 342: Config)}]), 343: ?line Ets = ets:new(ets,[public]), 344: ?line multiple_processes(Dets,dets), 345: ?line multiple_processes(Ets,ets), 346: ok. 347: 348: multiple_processes(Tab, Mod) -> 349: ?line io:format("Mod = ~p\n", [Mod]), 350: ?line P1 = start_commander(), 351: ?line P2 = start_commander(), 352: ?line false = Mod:info(Tab,fixed), 353: ?line false = Mod:info(Tab, safe_fixed), 354: ?line command(P1, {Mod, safe_fixtable, [Tab,true]}), 355: ?line true = Mod:info(Tab,fixed), 356: ?line {_,[{P1,1}]} = Mod:info(Tab, safe_fixed), 357: ?line command(P2, {Mod, safe_fixtable, [Tab,true]}), 358: ?line true = Mod:info(Tab,fixed), 359: ?line {_,L} = Mod:info(Tab,safe_fixed), 360: ?line true = (lists:sort(L) == lists:sort([{P1,1},{P2,1}])), 361: ?line command(P2, {Mod, safe_fixtable, [Tab,true]}), 362: ?line {_,L2} = Mod:info(Tab,safe_fixed), 363: ?line true = (lists:sort(L2) == lists:sort([{P1,1},{P2,2}])), 364: ?line command(P2, {Mod, safe_fixtable, [Tab,false]}), 365: ?line true = Mod:info(Tab,fixed), 366: ?line {_,L3} = Mod:info(Tab,safe_fixed), 367: ?line true = (lists:sort(L3) == lists:sort([{P1,1},{P2,1}])), 368: ?line command(P2, {Mod, safe_fixtable, [Tab,false]}), 369: ?line true = Mod:info(Tab,fixed), 370: ?line {_,[{P1,1}]} = Mod:info(Tab, safe_fixed), 371: ?line stop_commander(P1), 372: ?line receive after 1000 -> ok end, 373: ?line false = Mod:info(Tab,fixed), 374: ?line false = Mod:info(Tab, safe_fixed), 375: ?line command(P2, {Mod, safe_fixtable, [Tab,true]}), 376: ?line true = Mod:info(Tab,fixed), 377: ?line {_,[{P2,1}]} = Mod:info(Tab, safe_fixed), 378: case Mod of 379: dets -> 380: ?line dets:close(Tab); 381: ets -> 382: ?line ets:delete(Tab) 383: end, 384: ?line stop_commander(P2), 385: ?line receive after 1000 -> ok end, 386: ?line undefined = Mod:info(Tab, safe_fixed), 387: ok. 388: 389: 390: 391: %%% Helpers 392: dets_filename(Base, Config) when is_atom(Base) -> 393: dets_filename(atom_to_list(Base) ++ ".dat", Config); 394: dets_filename(Basename, Config) -> 395: PrivDir = ?config(priv_dir,Config), 396: filename:join(PrivDir, Basename). 397: 398: command_loop() -> 399: receive 400: {From, command, {M,F,A}} -> 401: Res = (catch apply(M, F, A)), 402: From ! {self(), Res}, 403: command_loop(); 404: die -> 405: ok 406: end. 407: 408: start_commander() -> 409: spawn(?MODULE, command_loop, []). 410: 411: stop_commander(Pid) -> 412: process_flag(trap_exit, true), 413: link(Pid), 414: Pid ! die, 415: receive 416: {'EXIT',Pid,_} -> 417: timer:sleep(1), % let other processes handle the signal as well 418: true 419: after 5000 -> 420: exit(stop_timeout) 421: end. 422: 423: command(Pid,MFA) -> 424: Pid ! {self(), command, MFA}, 425: receive 426: {Pid, Res} -> 427: Res 428: after 20000 -> 429: exit(command_timeout) 430: end. 431: 432: 433: