diff options
Diffstat (limited to 'lib/stdlib/test')
33 files changed, 1224 insertions, 445 deletions
diff --git a/lib/stdlib/test/Makefile b/lib/stdlib/test/Makefile index 5502c69fa5..aa6a660c34 100644 --- a/lib/stdlib/test/Makefile +++ b/lib/stdlib/test/Makefile @@ -65,6 +65,7 @@ MODULES= \ stdlib_SUITE \ string_SUITE \ supervisor_1 \ + supervisor_2 \ naughty_child \ shell_SUITE \ supervisor_SUITE \ diff --git a/lib/stdlib/test/beam_lib_SUITE.erl b/lib/stdlib/test/beam_lib_SUITE.erl index 4ccc863795..5df19ca7f1 100644 --- a/lib/stdlib/test/beam_lib_SUITE.erl +++ b/lib/stdlib/test/beam_lib_SUITE.erl @@ -181,7 +181,8 @@ error(Conf) when is_list(Conf) -> ?line verify(not_a_beam_file, beam_lib:info(<<"short">>)), ?line {Binary1, _} = split_binary(Binary, byte_size(Binary)-10), - ?line verify(chunk_too_big, beam_lib:chunks(Binary1, ["Abst"])), + LastChunk = last_chunk(Binary), + ?line verify(chunk_too_big, beam_lib:chunks(Binary1, [LastChunk])), ?line Chunks = chunk_info(Binary), ?line {value, {_, AbstractStart, _}} = lists:keysearch("Abst", 1, Chunks), ?line {Binary2, _} = split_binary(Binary, AbstractStart), @@ -205,6 +206,12 @@ error(Conf) when is_list(Conf) -> ?line file:delete(ACopy), ok. +last_chunk(Bin) -> + L = beam_lib:info(Bin), + {chunks,Chunks} = lists:keyfind(chunks, 1, L), + {Last,_,_} = lists:last(Chunks), + Last. + do_error(BeamFile, ACopy) -> % evil tests ?line Chunks = chunk_info(BeamFile), @@ -242,8 +249,8 @@ cmp(doc) -> ["Compare contents of BEAM files and directories"]; cmp(Conf) when is_list(Conf) -> ?line PrivDir = ?privdir, - ?line Dir1 = filename:join(PrivDir, dir1), - ?line Dir2 = filename:join(PrivDir, dir2), + ?line Dir1 = filename:join(PrivDir, "dir1"), + ?line Dir2 = filename:join(PrivDir, "dir2"), ok = file:make_dir(Dir1), ok = file:make_dir(Dir2), @@ -292,8 +299,8 @@ cmp_literals(doc) -> ["Compare contents of BEAM files having literals"]; cmp_literals(Conf) when is_list(Conf) -> ?line PrivDir = ?privdir, - ?line Dir1 = filename:join(PrivDir, dir1), - ?line Dir2 = filename:join(PrivDir, dir2), + ?line Dir1 = filename:join(PrivDir, "dir1"), + ?line Dir2 = filename:join(PrivDir, "dir2"), ok = file:make_dir(Dir1), ok = file:make_dir(Dir2), @@ -330,6 +337,7 @@ strip(Conf) when is_list(Conf) -> ?line {Source2D1, BeamFile2D1} = make_beam(PrivDir, simple2, concat), ?line {Source3D1, BeamFile3D1} = make_beam(PrivDir, make_fun, make_fun), ?line {Source4D1, BeamFile4D1} = make_beam(PrivDir, constant, constant), + ?line {Source5D1, BeamFile5D1} = make_beam(PrivDir, lines, lines), ?line NoOfTables = length(ets:all()), ?line P0 = pps(), @@ -360,13 +368,25 @@ strip(Conf) when is_list(Conf) -> ?line {module, make_fun} = code:load_abs(filename:rootname(BeamFile3D1)), ?line {module, constant} = code:load_abs(filename:rootname(BeamFile4D1)), + %% check that line number information is still present after stripping + ?line {module, lines} = code:load_abs(filename:rootname(BeamFile5D1)), + ?line {'EXIT',{badarith,[{lines,t,1,Info}|_]}} = + (catch lines:t(atom)), + ?line true = code:delete(lines), + ?line false = code:purge(lines), + ?line {ok, {lines,BeamFile5D1}} = beam_lib:strip(BeamFile5D1), + ?line {module, lines} = code:load_abs(filename:rootname(BeamFile5D1)), + ?line {'EXIT',{badarith,[{lines,t,1,Info}|_]}} = + (catch lines:t(atom)), + ?line true = (P0 == pps()), ?line NoOfTables = length(ets:all()), ?line delete_files([SourceD1, BeamFileD1, Source2D1, BeamFile2D1, Source3D1, BeamFile3D1, - Source4D1, BeamFile4D1]), + Source4D1, BeamFile4D1, + Source5D1, BeamFile5D1]), ok. @@ -381,7 +401,7 @@ otp_6711(Conf) when is_list(Conf) -> (catch {a, beam_lib:strip_files([3])}), ?line PrivDir = ?privdir, - ?line Dir = filename:join(PrivDir, dir), + ?line Dir = filename:join(PrivDir, "dir"), ?line Lib = filename:join(Dir, "lib"), ?line App = filename:join(Lib, "app"), ?line EBin = filename:join(App, "ebin"), @@ -417,8 +437,8 @@ building(doc) -> "Testing building of BEAM files."; building(Conf) when is_list(Conf) -> ?line PrivDir = ?privdir, - ?line Dir1 = filename:join(PrivDir, b_dir1), - ?line Dir2 = filename:join(PrivDir, b_dir2), + ?line Dir1 = filename:join(PrivDir, "b_dir1"), + ?line Dir2 = filename:join(PrivDir, "b_dir2"), ok = file:make_dir(Dir1), ok = file:make_dir(Dir2), @@ -571,8 +591,18 @@ do_encrypted_abstr(Beam, Key) -> ?line {ok,{simple,[{"Abst",Abst}]}} = beam_lib:chunks(Beam, ["Abst"]), ?line {ok,cleared} = beam_lib:clear_crypto_key_fun(), + + %% Try to force a stop/start race. + ?line start_stop_race(10000), + ok. +start_stop_race(0) -> + ok; +start_stop_race(N) -> + {error,_} = beam_lib:crypto_key_fun(bad_fun), + undefined = beam_lib:clear_crypto_key_fun(), + start_stop_race(N-1). bad_fun(F) -> {error,E} = beam_lib:crypto_key_fun(F), @@ -688,7 +718,7 @@ chunk_info(File) -> Chunks. make_beam(Dir, Module, F) -> - ?line FileBase = filename:join(Dir, Module), + ?line FileBase = filename:join(Dir, atom_to_list(Module)), ?line Source = FileBase ++ ".erl", ?line BeamFile = FileBase ++ ".beam", ?line simple_file(Source, Module, F), @@ -773,6 +803,12 @@ simple_file(File, Module, constant2) -> "t(A) -> " " {a,b,[2,3],x,y}. "]), ok = file:write_file(File, B); +simple_file(File, Module, lines) -> + B = list_to_binary(["-module(", atom_to_list(Module), ").\n" + "-export([t/1]).\n" + "t(A) ->\n" + " A+1.\n"]), + ok = file:write_file(File, B); simple_file(File, Module, F) -> B = list_to_binary(["-module(", atom_to_list(Module), "). " "-export([t/0]). " diff --git a/lib/stdlib/test/dets_SUITE.erl b/lib/stdlib/test/dets_SUITE.erl index 9fcc9e6aaf..6f77cff2b9 100644 --- a/lib/stdlib/test/dets_SUITE.erl +++ b/lib/stdlib/test/dets_SUITE.erl @@ -34,6 +34,8 @@ -define(datadir(Conf), ?config(data_dir, Conf)). -endif. +-compile(r13). % OTP-9607 + -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, not_run/1, newly_started/1, basic_v8/1, basic_v9/1, @@ -53,7 +55,7 @@ simultaneous_open/1, insert_new/1, repair_continuation/1, otp_5487/1, otp_6206/1, otp_6359/1, otp_4738/1, otp_7146/1, otp_8070/1, otp_8856/1, otp_8898/1, otp_8899/1, otp_8903/1, - otp_8923/1]). + otp_8923/1, otp_9282/1, otp_9607/1]). -export([dets_dirty_loop/0]). @@ -112,7 +114,7 @@ all() -> many_clients, otp_4906, otp_5402, simultaneous_open, insert_new, repair_continuation, otp_5487, otp_6206, otp_6359, otp_4738, otp_7146, otp_8070, otp_8856, otp_8898, - otp_8899, otp_8903, otp_8923] + otp_8899, otp_8903, otp_8923, otp_9282, otp_9607] end. groups() -> @@ -554,7 +556,11 @@ dets_dirty_loop() -> {From, [write, Name, Value]} -> Ret = dets:insert(Name, Value), From ! {self(), Ret}, - dets_dirty_loop() + dets_dirty_loop(); + {From, [close, Name]} -> + Ret = dets:close(Name), + From ! {self(), Ret}, + dets_dirty_loop() end. @@ -1516,7 +1522,7 @@ repair(Config, V) -> if V =:= 8 -> %% first estimated number of objects is wrong, repair once more - ?line {ok, Fd} = file:open(Fname, read_write), + ?line {ok, Fd} = file:open(Fname, [read,write]), NoPos = HeadSize - 8, % no_objects ?line file:pwrite(Fd, NoPos, <<0:32>>), % NoItems ok = file:close(Fd), @@ -1568,8 +1574,10 @@ repair(Config, V) -> ?line FileSize = dets:info(TabRef, memory), ?line ok = dets:close(TabRef), crash(Fname, FileSize+20), - ?line {error, {bad_freelists, Fname}} = + %% Used to return bad_freelists, but that changed in OTP-9622 + ?line {ok, TabRef} = dets:open_file(TabRef, [{file,Fname},{version,V}]), + ?line ok = dets:close(TabRef), ?line file:delete(Fname), %% File not closed, opening with read and read_write access tried. @@ -1857,10 +1865,10 @@ fixtable(Config, Version) when is_list(Config) -> ?line {ok, _} = dets:open_file(T, Args), %% badarg - ?line {'EXIT', {badarg, [{dets,safe_fixtable,[no_table,true]}|_]}} = - (catch dets:safe_fixtable(no_table,true)), - ?line {'EXIT', {badarg, [{dets,safe_fixtable,[T,undefined]}|_]}} = - (catch dets:safe_fixtable(T,undefined)), + ?line check_badarg(catch dets:safe_fixtable(no_table,true), + dets, safe_fixtable, [no_table,true]), + ?line check_badarg(catch dets:safe_fixtable(T,undefined), + dets, safe_fixtable, [T,undefined]), %% The table is not allowed to grow while the elements are inserted: @@ -1940,22 +1948,22 @@ match(Config, Version) -> %% match, badarg MSpec = [{'_',[],['$_']}], - ?line {'EXIT', {badarg, [{dets,safe_fixtable,[no_table,true]}|_]}} = - (catch dets:match(no_table, '_')), - ?line {'EXIT', {badarg, [{dets,match,[T,'_',not_a_number]}|_]}} = - (catch dets:match(T, '_', not_a_number)), + ?line check_badarg(catch dets:match(no_table, '_'), + dets, safe_fixtable, [no_table,true]), + ?line check_badarg(catch dets:match(T, '_', not_a_number), + dets, match, [T,'_',not_a_number]), ?line {EC1, _} = dets:select(T, MSpec, 1), - ?line {'EXIT', {badarg, [{dets,match,[EC1]}|_]}} = - (catch dets:match(EC1)), + ?line check_badarg(catch dets:match(EC1), + dets, match, [EC1]), %% match_object, badarg - ?line {'EXIT', {badarg, [{dets,safe_fixtable,[no_table,true]}|_]}} = - (catch dets:match_object(no_table, '_')), - ?line {'EXIT', {badarg, [{dets,match_object,[T,'_',not_a_number]}|_]}} = - (catch dets:match_object(T, '_', not_a_number)), + ?line check_badarg(catch dets:match_object(no_table, '_'), + dets, safe_fixtable, [no_table,true]), + ?line check_badarg(catch dets:match_object(T, '_', not_a_number), + dets, match_object, [T,'_',not_a_number]), ?line {EC2, _} = dets:select(T, MSpec, 1), - ?line {'EXIT', {badarg, [{dets,match_object,[EC2]}|_]}} = - (catch dets:match_object(EC2)), + ?line check_badarg(catch dets:match_object(EC2), + dets, match_object, [EC2]), dets:safe_fixtable(T, true), ?line {[_, _], C1} = dets:match_object(T, '_', 2), @@ -2118,17 +2126,17 @@ select(Config, Version) -> %% badarg MSpec = [{'_',[],['$_']}], - ?line {'EXIT', {badarg, [{dets,safe_fixtable,[no_table,true]}|_]}} = - (catch dets:select(no_table, MSpec)), - ?line {'EXIT', {badarg, [{dets,select,[T,<<17>>]}|_]}} = - (catch dets:select(T, <<17>>)), - ?line {'EXIT', {badarg, [{dets,select,[T,[]]}|_]}} = - (catch dets:select(T, [])), - ?line {'EXIT', {badarg, [{dets,select,[T,MSpec,not_a_number]}|_]}} = - (catch dets:select(T, MSpec, not_a_number)), + ?line check_badarg(catch dets:select(no_table, MSpec), + dets, safe_fixtable, [no_table,true]), + ?line check_badarg(catch dets:select(T, <<17>>), + dets, select, [T,<<17>>]), + ?line check_badarg(catch dets:select(T, []), + dets, select, [T,[]]), + ?line check_badarg(catch dets:select(T, MSpec, not_a_number), + dets, select, [T,MSpec,not_a_number]), ?line {EC, _} = dets:match(T, '_', 1), - ?line {'EXIT', {badarg, [{dets,select,[EC]}|_]}} = - (catch dets:select(EC)), + ?line check_badarg(catch dets:select(EC), + dets, select, [EC]), AllSpec = [{'_',[],['$_']}], @@ -2210,8 +2218,8 @@ update_counter(Config) when is_list(Config) -> ?line file:delete(Fname), P0 = pps(), - ?line {'EXIT', {badarg, [{dets,update_counter,[no_table,1,1]}|_]}} = - (catch dets:update_counter(no_table, 1, 1)), + ?line check_badarg(catch dets:update_counter(no_table, 1, 1), + dets, update_counter, [no_table,1,1]), Args = [{file,Fname},{keypos,2}], ?line {ok, _} = dets:open_file(T, [{type,set} | Args]), @@ -2254,66 +2262,66 @@ badarg(Config) when is_list(Config) -> %% badargs are tested in match, select and fixtable too. %% open - ?line {'EXIT', {badarg, [{dets,open_file,[{a,tuple},[]]}|_]}} = - (catch dets:open_file({a,tuple},[])), - ?line {'EXIT', {badarg, [{dets,open_file,[{a,tuple}]}|_]}} = - (catch dets:open_file({a,tuple})), - ?line {'EXIT', {badarg, [{dets,open_file,[file,[foo]]}|_]}} = - (catch dets:open_file(file,[foo])), - ?line {'EXIT', {badarg,[{dets,open_file,[{hej,san},[{type,set}|3]]}|_]}} = - (catch dets:open_file({hej,san},[{type,set}|3])), + ?line check_badarg(catch dets:open_file({a,tuple},[]), + dets, open_file, [{a,tuple},[]]), + ?line check_badarg(catch dets:open_file({a,tuple}), + dets, open_file,[{a,tuple}]), + ?line check_badarg(catch dets:open_file(file,[foo]), + dets, open_file, [file,[foo]]), + ?line check_badarg(catch dets:open_file({hej,san},[{type,set}|3]), + dets, open_file, [{hej,san},[{type,set}|3]]), %% insert - ?line {'EXIT', {badarg, [{dets,insert,[no_table,{1,2}]}|_]}} = - (catch dets:insert(no_table, {1,2})), - ?line {'EXIT', {badarg, [{dets,insert,[no_table,[{1,2}]]}|_]}} = - (catch dets:insert(no_table, [{1,2}])), - ?line {'EXIT', {badarg, [{dets,insert,[T,{1,2}]}|_]}} = - (catch dets:insert(T, {1,2})), - ?line {'EXIT', {badarg, [{dets,insert,[T,[{1,2}]]}|_]}} = - (catch dets:insert(T, [{1,2}])), - ?line {'EXIT', {badarg, [{dets,insert,[T,[{1,2,3}|3]]}|_]}} = - (catch dets:insert(T, [{1,2,3} | 3])), + ?line check_badarg(catch dets:insert(no_table, {1,2}), + dets, insert, [no_table,{1,2}]), + ?line check_badarg(catch dets:insert(no_table, [{1,2}]), + dets, insert, [no_table,[{1,2}]]), + ?line check_badarg(catch dets:insert(T, {1,2}), + dets, insert, [T,{1,2}]), + ?line check_badarg(catch dets:insert(T, [{1,2}]), + dets, insert, [T,[{1,2}]]), + ?line check_badarg(catch dets:insert(T, [{1,2,3} | 3]), + dets, insert, [T,[{1,2,3}|3]]), %% lookup{_keys} - ?line {'EXIT', {badarg, [{dets,lookup_keys,[badarg,[]]}|_]}} = - (catch dets:lookup_keys(T, [])), - ?line {'EXIT', {badarg, [{dets,lookup,[no_table,1]}|_]}} = - (catch dets:lookup(no_table, 1)), - ?line {'EXIT', {badarg, [{dets,lookup_keys,[T,[1|2]]}|_]}} = - (catch dets:lookup_keys(T, [1 | 2])), + ?line check_badarg(catch dets:lookup_keys(T, []), + dets, lookup_keys, [badarg,[]]), + ?line check_badarg(catch dets:lookup(no_table, 1), + dets, lookup, [no_table,1]), + ?line check_badarg(catch dets:lookup_keys(T, [1 | 2]), + dets, lookup_keys, [T,[1|2]]), %% member - ?line {'EXIT', {badarg, [{dets,member,[no_table,1]}|_]}} = - (catch dets:member(no_table, 1)), + ?line check_badarg(catch dets:member(no_table, 1), + dets, member, [no_table,1]), %% sync - ?line {'EXIT', {badarg, [{dets,sync,[no_table]}|_]}} = - (catch dets:sync(no_table)), + ?line check_badarg(catch dets:sync(no_table), + dets, sync, [no_table]), %% delete{_keys} - ?line {'EXIT', {badarg, [{dets,delete,[no_table,1]}|_]}} = - (catch dets:delete(no_table, 1)), + ?line check_badarg(catch dets:delete(no_table, 1), + dets, delete, [no_table,1]), %% delete_object - ?line {'EXIT', {badarg, [{dets,delete_object,[no_table,{1,2,3}]}|_]}} = - (catch dets:delete_object(no_table, {1,2,3})), - ?line {'EXIT', {badarg, [{dets,delete_object,[T,{1,2}]}|_]}} = - (catch dets:delete_object(T, {1,2})), - ?line {'EXIT', {badarg, [{dets,delete_object,[no_table,[{1,2,3}]]}|_]}} = - (catch dets:delete_object(no_table, [{1,2,3}])), - ?line {'EXIT', {badarg, [{dets,delete_object,[T,[{1,2}]]}|_]}} = - (catch dets:delete_object(T, [{1,2}])), - ?line {'EXIT', {badarg, [{dets,delete_object,[T,[{1,2,3}|3]]}|_]}} = - (catch dets:delete_object(T, [{1,2,3} | 3])), + ?line check_badarg(catch dets:delete_object(no_table, {1,2,3}), + dets, delete_object, [no_table,{1,2,3}]), + ?line check_badarg(catch dets:delete_object(T, {1,2}), + dets, delete_object, [T,{1,2}]), + ?line check_badarg(catch dets:delete_object(no_table, [{1,2,3}]), + dets, delete_object, [no_table,[{1,2,3}]]), + ?line check_badarg(catch dets:delete_object(T, [{1,2}]), + dets, delete_object, [T,[{1,2}]]), + ?line check_badarg(catch dets:delete_object(T, [{1,2,3} | 3]), + dets, delete_object, [T,[{1,2,3}|3]]), %% first,next,slot - ?line {'EXIT', {badarg, [{dets,first,[no_table]}|_]}} = - (catch dets:first(no_table)), - ?line {'EXIT', {badarg, [{dets,next,[no_table,1]}|_]}} = - (catch dets:next(no_table, 1)), - ?line {'EXIT', {badarg, [{dets,slot,[no_table,0]}|_]}} = - (catch dets:slot(no_table, 0)), + ?line check_badarg(catch dets:first(no_table), + dets, first, [no_table]), + ?line check_badarg(catch dets:next(no_table, 1), + dets, next, [no_table,1]), + ?line check_badarg(catch dets:slot(no_table, 0), + dets, slot, [no_table,0]), %% info ?line undefined = dets:info(no_table), @@ -2321,27 +2329,27 @@ badarg(Config) when is_list(Config) -> ?line undefined = dets:info(T, foo), %% match_delete - ?line {'EXIT', {badarg, [{dets,safe_fixtable,[no_table,true]}|_]}} = - (catch dets:match_delete(no_table, '_')), + ?line check_badarg(catch dets:match_delete(no_table, '_'), + dets, safe_fixtable, [no_table,true]), %% delete_all_objects - ?line {'EXIT', {badarg, [{dets,delete_all_objects,[no_table]}|_]}} = - (catch dets:delete_all_objects(no_table)), + ?line check_badarg(catch dets:delete_all_objects(no_table), + dets, delete_all_objects, [no_table]), %% select_delete MSpec = [{'_',[],['$_']}], - ?line {'EXIT', {badarg, [{dets,safe_fixtable,[no_table,true]}|_]}} = - (catch dets:select_delete(no_table, MSpec)), - ?line {'EXIT', {badarg, [{dets,select_delete,[T, <<17>>]}|_]}} = - (catch dets:select_delete(T, <<17>>)), + ?line check_badarg(catch dets:select_delete(no_table, MSpec), + dets, safe_fixtable, [no_table,true]), + ?line check_badarg(catch dets:select_delete(T, <<17>>), + dets, select_delete, [T, <<17>>]), %% traverse, fold - ?line {'EXIT', {badarg, [{dets,safe_fixtable,[no_table,true]}|_]}} = - (catch dets:traverse(no_table, fun(_) -> continue end)), - ?line {'EXIT', {badarg, [{dets,safe_fixtable,[no_table,true]}|_]}} = - (catch dets:foldl(fun(_, A) -> A end, [], no_table)), - ?line {'EXIT', {badarg, [{dets,safe_fixtable,[no_table,true]}|_]}} = - (catch dets:foldr(fun(_, A) -> A end, [], no_table)), + ?line check_badarg(catch dets:traverse(no_table, fun(_) -> continue end), + dets, safe_fixtable, [no_table,true]), + ?line check_badarg(catch dets:foldl(fun(_, A) -> A end, [], no_table), + dets, safe_fixtable, [no_table,true]), + ?line check_badarg(catch dets:foldr(fun(_, A) -> A end, [], no_table), + dets, safe_fixtable, [no_table,true]), %% close ?line ok = dets:close(T), @@ -2349,15 +2357,16 @@ badarg(Config) when is_list(Config) -> ?line {error, not_owner} = dets:close(T), %% init_table - ?line {'EXIT', {badarg,[{dets,init_table,[no_table,_,[]]}|_]}} = - (catch dets:init_table(no_table, fun(X) -> X end)), - ?line {'EXIT', {badarg,[{dets,init_table,[no_table,_,[]]}|_]}} = - (catch dets:init_table(no_table, fun(X) -> X end, [])), + IF = fun(X) -> X end, + ?line check_badarg(catch dets:init_table(no_table, IF), + dets, init_table, [no_table,IF,[]]), + ?line check_badarg(catch dets:init_table(no_table, IF, []), + dets, init_table, [no_table,IF,[]]), %% from_ets Ets = ets:new(ets,[]), - ?line {'EXIT', {badarg,[{dets,from_ets,[no_table,_]}|_]}} = - (catch dets:from_ets(no_table, Ets)), + ?line check_badarg(catch dets:from_ets(no_table, Ets), + dets, from_ets, [no_table,Ets]), ets:delete(Ets), ?line {ok, T} = dets:open_file(T, Args), @@ -3247,7 +3256,7 @@ otp_5402(suite) -> []; otp_5402(Config) when is_list(Config) -> Tab = otp_5402, - ?line File = filename:join([cannot, write, this, file]), + ?line File = filename:join(["cannot", "write", "this", "file"]), %% close ?line{ok, T} = dets:open_file(Tab, [{ram_file,true}, @@ -3857,15 +3866,118 @@ otp_8923(Config) when is_list(Config) -> file:delete(File), ok. +otp_9282(doc) -> + ["OTP-9282. The name of a table can be an arbitrary term"]; +otp_9282(suite) -> + []; +otp_9282(Config) when is_list(Config) -> + some_calls(make_ref(), Config), + some_calls({a,typical,name}, Config), + some_calls(fun() -> a_funny_name end, Config), + ok. + +some_calls(Tab, Config) -> + File = filename(ref, Config), + ?line {ok,T} = dets:open_file(Tab, [{file,File}]), + ?line T = Tab, + ?line false = dets:info(T, safe_fixed), + ?line File = dets:info(T, filename), + ?line ok = dets:insert(Tab, [{3,0}]), + ?line [{3,0}] = dets:lookup(Tab, 3), + ?line [{3,0}] = dets:traverse(Tab, fun(X) -> {continue, X} end), + ?line ok = dets:close(T), + file:delete(File). + +otp_9607(doc) -> + ["OTP-9607. Test downgrading the slightly changed format."]; +otp_9607(suite) -> + []; +otp_9607(Config) when is_list(Config) -> + %% Note: the bug is about almost full tables. The fix of that + %% problem is *not* tested here. + Version = r13b, + case ?t:is_release_available(atom_to_list(Version)) of + true -> + T = otp_9607, + File = filename(T, Config), + Key = a, + Value = 1, + Args = [{file,File}], + ?line {ok, T} = dets:open_file(T, Args), + ?line ok = dets:insert(T, {Key, Value}), + ?line ok = dets:close(T), + + ?line Call = fun(P, A) -> + P ! {self(), A}, + receive + {P, Ans} -> + Ans + after 5000 -> + exit(other_process_dead) + end + end, + %% Create a file on the modified format, read the file + %% with an emulator that doesn't know about the modified + %% format. + ?line {ok, Node} = start_node_rel(Version, Version, slave), + ?line Pid = rpc:call(Node, erlang, spawn, + [?MODULE, dets_dirty_loop, []]), + ?line {error,{needs_repair, File}} = + Call(Pid, [open, T, Args++[{repair,false}]]), + io:format("Expect repair:~n"), + ?line {ok, T} = Call(Pid, [open, T, Args]), + ?line [{Key,Value}] = Call(Pid, [read, T, Key]), + ?line ok = Call(Pid, [close, T]), + file:delete(File), + + %% Create a file on the unmodified format. Modify the file + %% using an emulator that must not turn the file into the + %% modified format. Read the file and make sure it is not + %% repaired. + ?line {ok, T} = Call(Pid, [open, T, Args]), + ?line ok = Call(Pid, [write, T, {Key,Value}]), + ?line [{Key,Value}] = Call(Pid, [read, T, Key]), + ?line ok = Call(Pid, [close, T]), + + Key2 = b, + Value2 = 2, + + ?line {ok, T} = dets:open_file(T, Args), + ?line [{Key,Value}] = dets:lookup(T, Key), + ?line ok = dets:insert(T, {Key2,Value2}), + ?line ok = dets:close(T), + + ?line {ok, T} = Call(Pid, [open, T, Args++[{repair,false}]]), + ?line [{Key2,Value2}] = Call(Pid, [read, T, Key2]), + ?line ok = Call(Pid, [close, T]), + + ?t:stop_node(Node), + file:delete(File), + ok; + false -> + {skipped, "No support for old node"} + end. + + + %% %% Parts common to several test cases %% +start_node_rel(Name, Rel, How) -> + Release = [{release, atom_to_list(Rel)}], + ?line Pa = filename:dirname(code:which(?MODULE)), + ?line test_server:start_node(Name, How, + [{args, + " -kernel net_setuptime 100 " + " -pa " ++ Pa}, + {erl, Release}]). + crash(File, Where) -> crash(File, Where, 10). crash(File, Where, What) when is_integer(What) -> - ?line {ok, Fd} = file:open(File, read_write), + ?line {ok, Fd} = file:open(File, [read,write]), ?line file:position(Fd, Where), ?line ok = file:write(Fd, [What]), ?line ok = file:close(Fd). @@ -4009,7 +4121,7 @@ writable(Fname) -> ?line file:write_file_info(Fname, Info#file_info{mode = Mode}). truncate(File, Where) -> - ?line {ok, Fd} = file:open(File, read_write), + ?line {ok, Fd} = file:open(File, [read,write]), ?line file:position(Fd, Where), ?line ok = file:truncate(Fd), ?line ok = file:close(Fd). @@ -4246,6 +4358,11 @@ bad_object({error,{{bad_object,_}, FileName}}, FileName) -> bad_object({error,{{{bad_object,_,_},_,_,_}, FileName}}, FileName) -> ok. % Debug. +check_badarg({'EXIT', {badarg, [{M,F,Args,_} | _]}}, M, F, Args) -> + true; +check_badarg({'EXIT', {badarg, [{M,F,A,_} | _]}}, M, F, Args) -> + true = test_server:is_native(M) andalso length(Args) =:= A. + check_pps(P0) -> case pps() of P0 -> diff --git a/lib/stdlib/test/epp_SUITE.erl b/lib/stdlib/test/epp_SUITE.erl index 9b024a5b49..f79414db49 100644 --- a/lib/stdlib/test/epp_SUITE.erl +++ b/lib/stdlib/test/epp_SUITE.erl @@ -20,7 +20,7 @@ -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2]). --export([rec_1/1, predef_mac/1, +-export([rec_1/1, include_local/1, predef_mac/1, upcase_mac_1/1, upcase_mac_2/1, variable_1/1, otp_4870/1, otp_4871/1, otp_5362/1, pmod/1, not_circular/1, skip_header/1, otp_6277/1, otp_7702/1, @@ -63,7 +63,7 @@ end_per_testcase(_, Config) -> suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - [rec_1, {group, upcase_mac}, predef_mac, + [rec_1, {group, upcase_mac}, include_local, predef_mac, {group, variable}, otp_4870, otp_4871, otp_5362, pmod, not_circular, skip_header, otp_6277, otp_7702, otp_8130, overload_mac, otp_8388, otp_8470, otp_8503, otp_8562, @@ -97,6 +97,22 @@ rec_1(Config) when is_list(Config) -> ?line check_errors(List), ok. +include_local(doc) -> + []; +include_local(suite) -> + []; +include_local(Config) when is_list(Config) -> + ?line DataDir = ?config(data_dir, Config), + ?line File = filename:join(DataDir, "include_local.erl"), + %% include_local.erl includes include/foo.hrl which + %% includes bar.hrl (also in include/) without requiring + %% any additional include path, and overriding any file + %% of the same name that the path points to + ?line {ok, List} = epp:parse_file(File, [DataDir], []), + ?line {value, {attribute,_,a,{true,true}}} = + lists:keysearch(a,3,List), + ok. + %%% Here is a little reimplementation of epp:parse_file, which times out %%% after 4 seconds if the epp server doesn't respond. If we use the %%% regular epp:parse_file, the test case will time out, and then epp @@ -234,16 +250,23 @@ otp_4871(Config) when is_list(Config) -> %% so there are some sanity checks before killing. ?line {ok,Epp} = epp:open(File, []), timer:sleep(1), - ?line {current_function,{epp,_,_}} = process_info(Epp, current_function), + ?line true = current_module(Epp, epp), ?line {monitored_by,[Io]} = process_info(Epp, monitored_by), - ?line {current_function,{file_io_server,_,_}} = - process_info(Io, current_function), + ?line true = current_module(Io, file_io_server), ?line exit(Io, emulate_crash), timer:sleep(1), ?line {error,{_Line,epp,cannot_parse}} = otp_4871_parse_file(Epp), ?line epp:close(Epp), ok. +current_module(Pid, Mod) -> + case process_info(Pid, current_function) of + {current_function, undefined} -> + true = test_server:is_native(Mod); + {current_function, {Mod, _, _}} -> + true + end. + otp_4871_parse_file(Epp) -> case epp:parse_erl_form(Epp) of {ok,_} -> otp_4871_parse_file(Epp); @@ -1280,7 +1303,7 @@ eval_tests(Config, Fun, Tests) -> check_test(Config, Test) -> - Filename = 'epp_test.erl', + Filename = "epp_test.erl", ?line PrivDir = ?config(priv_dir, Config), ?line File = filename:join(PrivDir, Filename), ?line ok = file:write_file(File, Test), @@ -1293,7 +1316,7 @@ check_test(Config, Test) -> compile_test(Config, Test0) -> Test = [<<"-module(epp_test). -compile(export_all). ">>, Test0], - Filename = 'epp_test.erl', + Filename = "epp_test.erl", ?line PrivDir = ?config(priv_dir, Config), ?line File = filename:join(PrivDir, Filename), ?line ok = file:write_file(File, Test), diff --git a/lib/stdlib/test/epp_SUITE_data/bar.hrl b/lib/stdlib/test/epp_SUITE_data/bar.hrl new file mode 100644 index 0000000000..01c527d549 --- /dev/null +++ b/lib/stdlib/test/epp_SUITE_data/bar.hrl @@ -0,0 +1,4 @@ +%% should not be included from include/foo.hrl even though the +%% include path points here - include/bar.hrl overrides it + +-define(BAR_HRL, false). diff --git a/lib/stdlib/test/epp_SUITE_data/include/bar.hrl b/lib/stdlib/test/epp_SUITE_data/include/bar.hrl new file mode 100644 index 0000000000..038d3c900e --- /dev/null +++ b/lib/stdlib/test/epp_SUITE_data/include/bar.hrl @@ -0,0 +1,3 @@ +%% included from foo.hrl in same directory + +-define(BAR_HRL, true). diff --git a/lib/stdlib/test/epp_SUITE_data/include/foo.hrl b/lib/stdlib/test/epp_SUITE_data/include/foo.hrl new file mode 100644 index 0000000000..a6dfa3d18a --- /dev/null +++ b/lib/stdlib/test/epp_SUITE_data/include/foo.hrl @@ -0,0 +1,4 @@ +%% includes bar.hrl in same directory + +-define(FOO_HRL, true). +-include("bar.hrl"). diff --git a/lib/stdlib/test/epp_SUITE_data/include_local.erl b/lib/stdlib/test/epp_SUITE_data/include_local.erl new file mode 100644 index 0000000000..c8e155a064 --- /dev/null +++ b/lib/stdlib/test/epp_SUITE_data/include_local.erl @@ -0,0 +1,6 @@ + +-module(include_local). + +-include("include/foo.hrl"). + +-a({?FOO_HRL, ?BAR_HRL}). diff --git a/lib/stdlib/test/erl_eval_SUITE.erl b/lib/stdlib/test/erl_eval_SUITE.erl index 4b59cee99e..369d8b224e 100644 --- a/lib/stdlib/test/erl_eval_SUITE.erl +++ b/lib/stdlib/test/erl_eval_SUITE.erl @@ -1036,6 +1036,12 @@ funs(Config) when is_list(Config) -> lists:usort([run_many_args(SAs) || SAs <- many_args(MaxArgs)]), ?line {'EXIT',{{argument_limit,_},_}} = (catch run_many_args(many_args1(MaxArgs+1))), + + ?line check(fun() -> M = lists, F = fun M:reverse/1, + [1,2] = F([2,1]), ok end, + "begin M = lists, F = fun M:reverse/1," + " [1,2] = F([2,1]), ok end.", + ok), ok. run_many_args({S, As}) -> @@ -1189,7 +1195,7 @@ lfh() -> {eval, fun(F, As, Bs) -> local_func(F, As, Bs) end}. local_func(F, As0, Bs0) when is_atom(F) -> - {As,Bs} = erl_eval:expr_list(As0, Bs0, {eval,lfh()}), + {As,Bs} = erl_eval:expr_list(As0, Bs0, lfh()), case erlang:function_exported(?MODULE, F, length(As)) of true -> {value,apply(?MODULE, F, As),Bs}; @@ -1199,7 +1205,7 @@ local_func(F, As0, Bs0) when is_atom(F) -> lfh_value_extra() -> %% Not documented. - {value, fun(F, As) -> local_func_value(F, As) end, []}. + {value, fun(F, As, a1, a2) -> local_func_value(F, As) end, [a1, a2]}. lfh_value() -> {value, fun(F, As) -> local_func_value(F, As) end}. diff --git a/lib/stdlib/test/erl_lint_SUITE.erl b/lib/stdlib/test/erl_lint_SUITE.erl index f980d52e4e..9041adbe5c 100644 --- a/lib/stdlib/test/erl_lint_SUITE.erl +++ b/lib/stdlib/test/erl_lint_SUITE.erl @@ -2981,7 +2981,7 @@ run_test(Conf, Test0, Warnings0) -> run_test2(Conf, Test, Warnings0). run_test2(Conf, Test, Warnings0) -> - Filename = 'lint_test.erl', + Filename = "lint_test.erl", DataDir = ?privdir, File = filename:join(DataDir, Filename), Opts = case Warnings0 of diff --git a/lib/stdlib/test/erl_pp_SUITE.erl b/lib/stdlib/test/erl_pp_SUITE.erl index bc811355ab..64853ca078 100644 --- a/lib/stdlib/test/erl_pp_SUITE.erl +++ b/lib/stdlib/test/erl_pp_SUITE.erl @@ -116,7 +116,6 @@ func(Config) when is_list(Config) -> {func_3, <<"t() -> fun t/0.">>}, {func_4, - %% Has already been expanded away in sys_pre_expand. <<"t() -> fun modul:foo/3.">>}, {func_5, % 'when' is moved down one line <<"tkjlksjflksdjflsdjlk() @@ -127,7 +126,9 @@ func(Config) when is_list(Config) -> <<"t() -> (fun() -> true - end)().">>} + end)().">>}, + {func_7, + <<"t(M, F, A) -> fun M:F/A.">>} ], ?line compile(Config, Ts), ok. @@ -1161,7 +1162,7 @@ parse_forms2(String, Cont0, Line, Forms) -> {done, {ok, Tokens, EndLine}, Chars} -> {ok, Form} = erl_parse:parse_form(Tokens), parse_forms2(Chars, [], EndLine, [Form | Forms]); - {more, Cont} when element(3, Cont) =:= [] -> + {more, Cont} when element(4, Cont) =:= [] -> %% extra spaces after forms... parse_forms2([], Cont, Line, Forms); {more, Cont} -> diff --git a/lib/stdlib/test/erl_scan_SUITE.erl b/lib/stdlib/test/erl_scan_SUITE.erl index 31a4f94294..4298b2c701 100644 --- a/lib/stdlib/test/erl_scan_SUITE.erl +++ b/lib/stdlib/test/erl_scan_SUITE.erl @@ -737,6 +737,10 @@ set_attribute() -> (catch {foo, erl_scan:set_attribute(line, [], F2)}), % type error ?line {'EXIT',{badarg,_}} = (catch {foo, erl_scan:set_attribute(column, [], F2)}), % type error + + %% OTP-9412 + ?line 8 = erl_scan:set_attribute(line, [{line,{nos,'X',8}}], + fun({nos,_V,VL}) -> VL end), ok. column_errors() -> diff --git a/lib/stdlib/test/ets_SUITE.erl b/lib/stdlib/test/ets_SUITE.erl index 9d348b5f1a..0e8849b5b3 100644 --- a/lib/stdlib/test/ets_SUITE.erl +++ b/lib/stdlib/test/ets_SUITE.erl @@ -72,8 +72,9 @@ exit_many_many_tables_owner/1]). -export([write_concurrency/1, heir/1, give_away/1, setopts/1]). -export([bad_table/1, types/1]). +-export([otp_9423/1]). --export([init_per_testcase/2, end_per_testcase/2]). +-export([init_per_testcase/2]). %% Convenience for manual testing -export([random_test/0]). @@ -143,7 +144,8 @@ all() -> otp_8166, exit_large_table_owner, exit_many_large_table_owner, exit_many_tables_owner, exit_many_many_tables_owner, write_concurrency, heir, - give_away, setopts, bad_table, types]. + give_away, setopts, bad_table, types, + otp_9423]. groups() -> [{new, [], @@ -174,6 +176,7 @@ groups() -> meta_newdel_unnamed, meta_newdel_named]}]. init_per_suite(Config) -> + erts_debug:set_internal_state(available_internal_state, true), Config. end_per_suite(_Config) -> @@ -302,7 +305,6 @@ t_match_spec_run(Config) when is_list(Config) -> end, repeat_for_permutations(F, N_MS) end, - test_terms(Fun, skip_refc_check), ?line verify_etsmem(EtsMem). @@ -322,7 +324,7 @@ t_match_spec_run_test(List, MS, Result) -> %% Check that tracing agree Self = self(), - {Tracee, MonRef} = spawn_monitor(fun() -> ms_tracee(Self, List) end), + {Tracee, MonRef} = my_spawn_monitor(fun() -> ms_tracee(Self, List) end), receive {Tracee, ready} -> ok end, MST = lists:map(fun(Clause) -> ms_clause_ets_to_trace(Clause) end, MS), @@ -583,7 +585,6 @@ select_fail_do(Opts) -> memory(doc) -> ["Whitebox test of ets:info(X,memory)"]; memory(suite) -> []; memory(Config) when is_list(Config) -> - ?line erts_debug:set_internal_state(available_internal_state, true), ?line ok = chk_normal_tab_struct_size(), repeat_for_opts(memory_do,[compressed]), ?line catch erts_debug:set_internal_state(available_internal_state, false). @@ -793,21 +794,26 @@ t_ets_dets(Config, Opts) -> ?line true = ets:from_dets(ETab,DTab), ?line 3000 = ets:info(ETab,size), ?line ets:delete(ETab), - ?line {'EXIT',{badarg,[{ets,to_dets,[ETab,DTab]}|_]}} = - (catch ets:to_dets(ETab,DTab)), - ?line {'EXIT',{badarg,[{ets,from_dets,[ETab,DTab]}|_]}} = - (catch ets:from_dets(ETab,DTab)), + ?line check_badarg(catch ets:to_dets(ETab,DTab), + ets, to_dets, [ETab,DTab]), + ?line check_badarg(catch ets:from_dets(ETab,DTab), + ets, from_dets, [ETab,DTab]), ?line ETab2 = ets_new(x,Opts), ?line filltabint(ETab2,3000), ?line dets:close(DTab), - ?line {'EXIT',{badarg,[{ets,to_dets,[ETab2,DTab]}|_]}} = - (catch ets:to_dets(ETab2,DTab)), - ?line {'EXIT',{badarg,[{ets,from_dets,[ETab2,DTab]}|_]}} = - (catch ets:from_dets(ETab2,DTab)), + ?line check_badarg(catch ets:to_dets(ETab2,DTab), + ets, to_dets, [ETab2,DTab]), + ?line check_badarg(catch ets:from_dets(ETab2,DTab), + ets, from_dets, [ETab2,DTab]), ?line ets:delete(ETab2), ?line (catch file:delete(Fname)), ok. +check_badarg({'EXIT', {badarg, [{M,F,Args,_} | _]}}, M, F, Args) -> + true; +check_badarg({'EXIT', {badarg, [{M,F,A,_} | _]}}, M, F, Args) -> + true = test_server:is_native(M) andalso length(Args) =:= A. + t_delete_all_objects(doc) -> ["Test ets:delete_all_objects/1"]; t_delete_all_objects(suite) -> @@ -817,6 +823,14 @@ t_delete_all_objects(Config) when is_list(Config) -> repeat_for_opts(t_delete_all_objects_do), ?line verify_etsmem(EtsMem). +get_kept_objects(T) -> + case ets:info(T,stats) of + false -> + 0; + {_,_,_,_,_,_,KO} -> + KO + end. + t_delete_all_objects_do(Opts) -> ?line T=ets_new(x,Opts), ?line filltabint(T,4000), @@ -826,10 +840,10 @@ t_delete_all_objects_do(Opts) -> ?line true = ets:delete_all_objects(T), ?line '$end_of_table' = ets:next(T,O), ?line 0 = ets:info(T,size), - ?line 4000 = ets:info(T,kept_objects), + ?line 4000 = get_kept_objects(T), ?line ets:safe_fixtable(T,false), ?line 0 = ets:info(T,size), - ?line 0 = ets:info(T,kept_objects), + ?line 0 = get_kept_objects(T), ?line filltabint(T,4000), ?line 4000 = ets:info(T,size), ?line true = ets:delete_all_objects(T), @@ -859,10 +873,10 @@ t_delete_object_do(Opts) -> ?line ets:delete_object(T,{First, integer_to_list(First)}), ?line Next = ets:next(T,First), ?line 3999 = ets:info(T,size), - ?line 1 = ets:info(T,kept_objects), + ?line 1 = get_kept_objects(T), ?line ets:safe_fixtable(T,false), ?line 3999 = ets:info(T,size), - ?line 0 = ets:info(T,kept_objects), + ?line 0 = get_kept_objects(T), ?line ets:delete(T), ?line T1 = ets_new(x,[ordered_set | Opts]), ?line filltabint(T1,4000), @@ -1932,7 +1946,7 @@ evil_update_counter(Config) when is_list(Config) -> evil_update_counter_do(Opts) -> ?line EtsMem = etsmem(), ?line process_flag(trap_exit, true), - ?line Pids = [spawn_link(fun() -> evil_counter(I,Opts) end) || I <- lists:seq(1, 40)], + ?line Pids = [my_spawn_link(fun() -> evil_counter(I,Opts) end) || I <- lists:seq(1, 40)], ?line wait_for_all(gb_sets:from_list(Pids)), ?line verify_etsmem(EtsMem), ok. @@ -2138,24 +2152,24 @@ heir_do(Opts) -> Combos), %% No heir - {Founder1,MrefF1} = spawn_monitor(fun()->heir_founder(Master,foo_data,Opts)end), + {Founder1,MrefF1} = my_spawn_monitor(fun()->heir_founder(Master,foo_data,Opts)end), Founder1 ! {go, none}, ?line {"No heir",Founder1} = receive_any(), ?line {'DOWN', MrefF1, process, Founder1, normal} = receive_any(), ?line undefined = ets:info(foo), %% An already dead heir - {Heir2,MrefH2} = spawn_monitor(fun()->die end), + {Heir2,MrefH2} = my_spawn_monitor(fun()->die end), ?line {'DOWN', MrefH2, process, Heir2, normal} = receive_any(), - {Founder2,MrefF2} = spawn_monitor(fun()->heir_founder(Master,foo_data,Opts)end), + {Founder2,MrefF2} = my_spawn_monitor(fun()->heir_founder(Master,foo_data,Opts)end), Founder2 ! {go, Heir2}, ?line {"No heir",Founder2} = receive_any(), ?line {'DOWN', MrefF2, process, Founder2, normal} = receive_any(), ?line undefined = ets:info(foo), %% When heir dies before founder - {Founder3,MrefF3} = spawn_monitor(fun()->heir_founder(Master,"The dying heir",Opts)end), - {Heir3,MrefH3} = spawn_monitor(fun()->heir_heir(Founder3)end), + {Founder3,MrefF3} = my_spawn_monitor(fun()->heir_founder(Master,"The dying heir",Opts)end), + {Heir3,MrefH3} = my_spawn_monitor(fun()->heir_heir(Founder3)end), Founder3 ! {go, Heir3}, ?line {'DOWN', MrefH3, process, Heir3, normal} = receive_any(), Founder3 ! die_please, @@ -2163,14 +2177,12 @@ heir_do(Opts) -> ?line undefined = ets:info(foo), %% When heir dies and pid reused before founder dies - erts_debug:set_internal_state(available_internal_state,true), NextPidIx = erts_debug:get_internal_state(next_pid), - {Founder4,MrefF4} = spawn_monitor(fun()->heir_founder(Master,"The dying heir",Opts)end), - {Heir4,MrefH4} = spawn_monitor(fun()->heir_heir(Founder4)end), + {Founder4,MrefF4} = my_spawn_monitor(fun()->heir_founder(Master,"The dying heir",Opts)end), + {Heir4,MrefH4} = my_spawn_monitor(fun()->heir_heir(Founder4)end), Founder4 ! {go, Heir4}, ?line {'DOWN', MrefH4, process, Heir4, normal} = receive_any(), erts_debug:set_internal_state(next_pid, NextPidIx), - erts_debug:set_internal_state(available_internal_state,false), {Heir4,MrefH4_B} = spawn_monitor_with_pid(Heir4, fun()-> ?line die_please = receive_any() end), Founder4 ! die_please, @@ -2246,9 +2258,9 @@ heir_heir(Founder, Mode) -> heir_1(HeirData,Mode,Opts) -> io:format("test with heir_data = ~p\n", [HeirData]), Master = self(), - ?line Founder = spawn_link(fun() -> heir_founder(Master,HeirData,Opts) end), + ?line Founder = my_spawn_link(fun() -> heir_founder(Master,HeirData,Opts) end), io:format("founder spawned = ~p\n", [Founder]), - ?line {Heir,Mref} = spawn_monitor(fun() -> heir_heir(Founder,Mode) end), + ?line {Heir,Mref} = my_spawn_monitor(fun() -> heir_heir(Founder,Mode) end), io:format("heir spawned = ~p\n", [{Heir,Mref}]), ?line Founder ! {go, Heir}, ?line {'DOWN', Mref, process, Heir, normal} = receive_any(). @@ -2265,7 +2277,7 @@ give_away_do(Opts) -> Parent = self(), %% Give and then give back - ?line {Receiver,Mref} = spawn_monitor(fun()-> give_away_receiver(T,Parent) end), + ?line {Receiver,Mref} = my_spawn_monitor(fun()-> give_away_receiver(T,Parent) end), ?line give_me = receive_any(), ?line true = ets:give_away(T,Receiver,here_you_are), ?line {'EXIT',{badarg,_}} = (catch ets:lookup(T,key)), @@ -2276,7 +2288,7 @@ give_away_do(Opts) -> %% Give and then let receiver keep it ?line true = ets:insert(T,{key,1}), - ?line {Receiver3,Mref3} = spawn_monitor(fun()-> give_away_receiver(T,Parent) end), + ?line {Receiver3,Mref3} = my_spawn_monitor(fun()-> give_away_receiver(T,Parent) end), ?line give_me = receive_any(), ?line true = ets:give_away(T,Receiver3,here_you_are), ?line {'EXIT',{badarg,_}} = (catch ets:lookup(T,key)), @@ -2288,7 +2300,7 @@ give_away_do(Opts) -> ?line T2 = ets_new(foo,[private | Opts]), ?line true = ets:insert(T2,{key,1}), ?line ets:setopts(T2,{heir,self(),"Som en gummiboll..."}), - ?line {Receiver2,Mref2} = spawn_monitor(fun()-> give_away_receiver(T2,Parent) end), + ?line {Receiver2,Mref2} = my_spawn_monitor(fun()-> give_away_receiver(T2,Parent) end), ?line give_me = receive_any(), ?line true = ets:give_away(T2,Receiver2,here_you_are), ?line {'EXIT',{badarg,_}} = (catch ets:lookup(T2,key)), @@ -2303,12 +2315,12 @@ give_away_do(Opts) -> ?line {'EXIT',{badarg,_}} = (catch ets:give_away(T2,"not a pid","To wrong type")), ?line true = ets:delete(T2), - ?line {ReceiverNeg,MrefNeg} = spawn_monitor(fun()-> give_away_receiver(T2,Parent) end), + ?line {ReceiverNeg,MrefNeg} = my_spawn_monitor(fun()-> give_away_receiver(T2,Parent) end), ?line give_me = receive_any(), ?line {'EXIT',{badarg,_}} = (catch ets:give_away(T2,ReceiverNeg,"A deleted table")), ?line T3 = ets_new(foo,[public | Opts]), - spawn_link(fun()-> {'EXIT',{badarg,_}} = (catch ets:give_away(T3,ReceiverNeg,"From non owner")), + my_spawn_link(fun()-> {'EXIT',{badarg,_}} = (catch ets:give_away(T3,ReceiverNeg,"From non owner")), Parent ! done end), ?line done = receive_any(), @@ -2344,7 +2356,7 @@ setopts_do(Opts) -> Self = self(), ?line T = ets_new(foo,[named_table, private | Opts]), ?line none = ets:info(T,heir), - Heir = spawn_link(fun()->heir_heir(Self) end), + Heir = my_spawn_link(fun()->heir_heir(Self) end), ?line ets:setopts(T,{heir,Heir,"Data"}), ?line Heir = ets:info(T,heir), ?line ets:setopts(T,{heir,self(),"Data"}), @@ -2395,14 +2407,14 @@ bad_table(Config) when is_list(Config) -> bad_table_do(Opts, DummyFile) -> Parent = self(), - {Pid,Mref} = spawn_opt(fun()-> ets_new(priv,[private,named_table | Opts]), - Priv = ets_new(priv,[private | Opts]), - ets_new(prot,[protected,named_table | Opts]), - Prot = ets_new(prot,[protected | Opts]), - Parent ! {self(),Priv,Prot}, - die_please = receive_any() - end, - [link, monitor]), + {Pid,Mref} = my_spawn_opt(fun()-> ets_new(priv,[private,named_table | Opts]), + Priv = ets_new(priv,[private | Opts]), + ets_new(prot,[protected,named_table | Opts]), + Prot = ets_new(prot,[protected | Opts]), + Parent ! {self(),Priv,Prot}, + die_please = receive_any() + end, + [link, monitor]), {Pid,Priv,Prot} = receive_any(), MatchSpec = {{key,'_'}, [], ['$$']}, Fun = fun(X,_) -> X end, @@ -2642,7 +2654,7 @@ maybe_sort(L) when is_list(L) -> %maybe_sort({'EXIT',{Reason, [{Module, Function, _}|_]}}) -> % {'EXIT',{Reason, [{Module, Function, '_'}]}}; maybe_sort({'EXIT',{Reason, List}}) when is_list(List) -> - {'EXIT',{Reason, lists:map(fun({Module, Function, _}) -> + {'EXIT',{Reason, lists:map(fun({Module, Function, _, _}) -> {Module, Function, '_'} end, List)}}; @@ -2715,7 +2727,8 @@ ordered_do(Opts) -> 9,10,11,12, 1,2,3,4, 17,18,19,20, - 13,14,15,16 + 13,14,15,16, + 1 bsl 33 ], ?line lists:foreach(fun(X) -> ets:insert(T,{X,integer_to_list(X)}) @@ -2730,13 +2743,14 @@ ordered_do(Opts) -> ?line S2 = L2, ?line [{1,"1"}] = ets:slot(T,0), ?line [{28,"28"}] = ets:slot(T,27), + ?line [{1 bsl 33,_}] = ets:slot(T,28), ?line 27 = ets:prev(T,28), ?line [{7,"7"}] = ets:slot(T,6), - ?line '$end_of_table' = ets:next(T,28), + ?line '$end_of_table' = ets:next(T,1 bsl 33), ?line [{12,"12"}] = ets:slot(T,11), - ?line '$end_of_table' = ets:slot(T,28), + ?line '$end_of_table' = ets:slot(T,29), ?line [{1,"1"}] = ets:slot(T,0), - ?line 28 = ets:prev(T,29), + ?line 28 = ets:prev(T,1 bsl 33), ?line 1 = ets:next(T,0), ?line pick_all_forward(T), ?line [{7,"7"}] = ets:slot(T,6), @@ -3246,7 +3260,7 @@ delete_large_named_table_1(Name, Flags, Data, Fix) -> ?line lists:foreach(fun({K,_}) -> ets:delete(Tab, K) end, Data) end, Parent = self(), - Pid = spawn_link(fun() -> + Pid = my_spawn_link(fun() -> receive {trace,Parent,call,_} -> ets_new(Name, [named_table]) @@ -3608,7 +3622,7 @@ cycle(Tab, L) -> ets:insert(Tab,list_to_tuple(L)), cycle(Tab, tl(L)++[hd(L)]). -dynamic_go() -> spawn_link(fun dynamic_init/0). +dynamic_go() -> my_spawn_link(fun dynamic_init/0). dynamic_init() -> [dyn_lookup(?MODULE) || _ <- lists:seq(1, 10)]. @@ -3835,7 +3849,7 @@ safe_fixtable_do(Opts) -> Self = self(), ?line {{_,_,_},[{Self,1}]} = ets:info(Tab,safe_fixed), %% Test that an unjustified 'unfix' is a no-op. - {Pid,MRef} = spawn_monitor(fun() -> true = ets:safe_fixtable(Tab,false) end), + {Pid,MRef} = my_spawn_monitor(fun() -> true = ets:safe_fixtable(Tab,false) end), {'DOWN', MRef, process, Pid, normal} = receive M -> M end, ?line true = ets:info(Tab,fixed), ?line {{_,_,_},[{Self,1}]} = ets:info(Tab,safe_fixed), @@ -4239,7 +4253,7 @@ do_heavy_concurrent(Opts) -> ?line ok = fill_tab2(Tab, 0, Size), ?line Procs = lists:map( fun (N) -> - spawn_link( + my_spawn_link( fun () -> do_heavy_concurrent_proc(Tab, Size, N) end) @@ -4843,12 +4857,7 @@ otp_7665_act(Tab,Min,Max,DelNr) -> %% Whitebox testing of meta name table hashing. meta_wb(Config) when is_list(Config) -> ?line EtsMem = etsmem(), - ?line erts_debug:set_internal_state(available_internal_state, true), - try - repeat_for_opts(meta_wb_do) - after - erts_debug:set_internal_state(available_internal_state, false) - end, + repeat_for_opts(meta_wb_do), ?line verify_etsmem(EtsMem). @@ -4917,12 +4926,15 @@ colliding_names(Name) -> grow_shrink(Config) when is_list(Config) -> ?line EtsMem = etsmem(), - grow_shrink_0(lists:seq(3071, 5000), EtsMem). + ?line grow_shrink_0(lists:seq(3071, 5000), EtsMem), + ?line verify_etsmem(EtsMem). grow_shrink_0([N|Ns], EtsMem) -> ?line grow_shrink_1(N, [set]), ?line grow_shrink_1(N, [ordered_set]), - ?line verify_etsmem(EtsMem), + %% Verifying ets-memory here takes too long time, since + %% lock-free allocators were introduced... + %% ?line verify_etsmem(EtsMem), grow_shrink_0(Ns, EtsMem); grow_shrink_0([], _) -> ok. @@ -4967,21 +4979,21 @@ grow_pseudo_deleted_do(Type) -> [true]}]), Left = Mult*(Mod-1), ?line Left = ets:info(T,size), - ?line Mult = ets:info(T,kept_objects), + ?line Mult = get_kept_objects(T), filltabstr(T,Mult), - spawn_opt(fun()-> ?line true = ets:info(T,fixed), - Self ! start, - io:format("Starting to filltabstr... ~p\n",[now()]), - filltabstr(T,Mult,Mult+10000), - io:format("Done with filltabstr. ~p\n",[now()]), - Self ! done - end, [link, {scheduler,2}]), + my_spawn_opt(fun()-> ?line true = ets:info(T,fixed), + Self ! start, + io:format("Starting to filltabstr... ~p\n",[now()]), + filltabstr(T,Mult,Mult+10000), + io:format("Done with filltabstr. ~p\n",[now()]), + Self ! done + end, [link, {scheduler,2}]), ?line start = receive_any(), io:format("Unfixing table...~p nitems=~p\n",[now(),ets:info(T,size)]), ?line true = ets:safe_fixtable(T,false), io:format("Unfix table done. ~p nitems=~p\n",[now(),ets:info(T,size)]), ?line false = ets:info(T,fixed), - ?line 0 = ets:info(T,kept_objects), + ?line 0 = get_kept_objects(T), ?line done = receive_any(), %%verify_table_load(T), % may fail if concurrency is poor (genny) ets:delete(T), @@ -5008,20 +5020,20 @@ shrink_pseudo_deleted_do(Type) -> [{'>', '$1', Half}], [true]}]), ?line Half = ets:info(T,size), - ?line Half = ets:info(T,kept_objects), - spawn_opt(fun()-> ?line true = ets:info(T,fixed), - Self ! start, - io:format("Starting to delete... ~p\n",[now()]), - del_one_by_one_set(T,1,Half+1), - io:format("Done with delete. ~p\n",[now()]), - Self ! done - end, [link, {scheduler,2}]), + ?line Half = get_kept_objects(T), + my_spawn_opt(fun()-> ?line true = ets:info(T,fixed), + Self ! start, + io:format("Starting to delete... ~p\n",[now()]), + del_one_by_one_set(T,1,Half+1), + io:format("Done with delete. ~p\n",[now()]), + Self ! done + end, [link, {scheduler,2}]), ?line start = receive_any(), io:format("Unfixing table...~p nitems=~p\n",[now(),ets:info(T,size)]), ?line true = ets:safe_fixtable(T,false), io:format("Unfix table done. ~p nitems=~p\n",[now(),ets:info(T,size)]), ?line false = ets:info(T,fixed), - ?line 0 = ets:info(T,kept_objects), + ?line 0 = get_kept_objects(T), ?line done = receive_any(), %%verify_table_load(T), % may fail if concurrency is poor (genny) ets:delete(T), @@ -5137,7 +5149,7 @@ smp_fixed_delete_do() -> ?line 0 = ets:info(T,size), ?line true = ets:info(T,fixed), ?line Buckets = num_of_buckets(T), - ?line NumOfObjs = ets:info(T,kept_objects), + ?line NumOfObjs = get_kept_objects(T), ets:safe_fixtable(T,false), %% Will fail as unfix does not shrink the table: %%?line Mem = ets:info(T,memory), @@ -5169,27 +5181,27 @@ smp_unfix_fix_do() -> Left = NumOfObjs - Deleted, ?line Left = ets:info(T,size), ?line true = ets:info(T,fixed), - ?line Deleted = ets:info(T,kept_objects), + ?line Deleted = get_kept_objects(T), {Child, Mref} = - spawn_opt(fun()-> ?line true = ets:info(T,fixed), - Parent ! start, - io:format("Child waiting for table to be unfixed... now=~p mem=~p\n", - [now(),ets:info(T,memory)]), - repeat_while(fun()-> ets:info(T,fixed) end), - io:format("Table unfixed. Child Fixating! now=~p mem=~p\n", - [now(),ets:info(T,memory)]), - ?line true = ets:safe_fixtable(T,true), - repeat_while(fun(Key) when Key =< NumOfObjs -> - ets:delete(T,Key), {true,Key+1}; - (Key) -> {false,Key} - end, - Deleted), - ?line 0 = ets:info(T,size), - ?line true = ets:info(T,kept_objects) >= Left, - ?line done = receive_any() - end, - [link, monitor, {scheduler,2}]), + my_spawn_opt(fun()-> ?line true = ets:info(T,fixed), + Parent ! start, + io:format("Child waiting for table to be unfixed... now=~p mem=~p\n", + [now(),ets:info(T,memory)]), + repeat_while(fun()-> ets:info(T,fixed) end), + io:format("Table unfixed. Child Fixating! now=~p mem=~p\n", + [now(),ets:info(T,memory)]), + ?line true = ets:safe_fixtable(T,true), + repeat_while(fun(Key) when Key =< NumOfObjs -> + ets:delete(T,Key), {true,Key+1}; + (Key) -> {false,Key} + end, + Deleted), + ?line 0 = ets:info(T,size), + ?line true = get_kept_objects(T) >= Left, + ?line done = receive_any() + end, + [link, monitor, {scheduler,2}]), ?line start = receive_any(), ?line true = ets:info(T,fixed), @@ -5199,7 +5211,7 @@ smp_unfix_fix_do() -> Child ! done, {'DOWN', Mref, process, Child, normal} = receive_any(), ?line false = ets:info(T,fixed), - ?line 0 = ets:info(T,kept_objects), + ?line 0 = get_kept_objects(T), %%verify_table_load(T), ets:delete(T), process_flag(scheduler,0). @@ -5220,11 +5232,11 @@ otp_8166_do(WC) -> Deleted = NumOfObjs div 2, filltabint(T,NumOfObjs), {ReaderPid, ReaderMref} = - spawn_opt(fun()-> otp_8166_reader(T,NumOfObjs) end, - [link, monitor, {scheduler,2}]), + my_spawn_opt(fun()-> otp_8166_reader(T,NumOfObjs) end, + [link, monitor, {scheduler,2}]), {ZombieCrPid, ZombieCrMref} = - spawn_opt(fun()-> otp_8166_zombie_creator(T,Deleted) end, - [link, monitor, {scheduler,3}]), + my_spawn_opt(fun()-> otp_8166_zombie_creator(T,Deleted) end, + [link, monitor, {scheduler,3}]), repeat(fun() -> ZombieCrPid ! {loop, self()}, zombies_created = receive_any(), @@ -5237,7 +5249,7 @@ otp_8166_do(WC) -> ZombieCrPid ! quit, {'DOWN', ZombieCrMref, process, ZombieCrPid, normal} = receive_any(), ?line false = ets:info(T,fixed), - ?line 0 = ets:info(T,kept_objects), + ?line 0 = get_kept_objects(T), %%verify_table_load(T), ets:delete(T), process_flag(scheduler,0). @@ -5304,7 +5316,7 @@ otp_8166_zombie_creator(T,Deleted) -> verify_table_load(T) -> ?line Stats = ets:info(T,stats), - ?line {Buckets,AvgLen,StdDev,ExpSD,_MinLen,_MaxLen} = Stats, + ?line {Buckets,AvgLen,StdDev,ExpSD,_MinLen,_MaxLen,_} = Stats, ?line ok = if AvgLen > 7 -> io:format("Table overloaded: Stats=~p\n~p\n", @@ -5420,7 +5432,39 @@ types_do(Opts) -> ?line verify_etsmem(EtsMem). - +otp_9423(doc) -> ["vm-deadlock caused by race between ets:delete and others on write_concurrency table"]; +otp_9423(Config) when is_list(Config) -> + InitF = fun(_) -> {0,0} end, + ExecF = fun({S,F}) -> + receive + stop -> + io:format("~p got stop\n", [self()]), + [end_of_work | {"Succeded=",S,"Failed=",F}] + after 0 -> + %%io:format("~p (~p) doing lookup\n", [self(), {S,F}]), + try ets:lookup(otp_9423, key) of + [] -> {S+1,F} + catch + error:badarg -> {S,F+1} + end + end + end, + FiniF = fun(R) -> R end, + case run_workers(InitF, ExecF, FiniF, infinite, 1) of + Pids when is_list(Pids) -> + %%[P ! start || P <- Pids], + repeat(fun() -> ets:new(otp_9423, [named_table, public, {write_concurrency,true}]), + ets:delete(otp_9423) + end, 10000), + [P ! stop || P <- Pids], + wait_pids(Pids), + ok; + + Skipped -> Skipped + end. + + + % % Utility functions: @@ -5434,21 +5478,30 @@ add_lists([E1|T1], [E2|T2], Acc) -> add_lists(T1, T2, [E1+E2 | Acc]). run_workers(InitF,ExecF,FiniF,Laps) -> + run_workers(InitF,ExecF,FiniF,Laps, 0). +run_workers(InitF,ExecF,FiniF,Laps, Exclude) -> case erlang:system_info(smp_support) of true -> - run_workers_do(InitF,ExecF,FiniF,Laps); + run_workers_do(InitF,ExecF,FiniF,Laps, Exclude); false -> {skipped,"No smp support"} end. - + run_workers_do(InitF,ExecF,FiniF,Laps) -> - NumOfProcs = erlang:system_info(schedulers), + run_workers_do(InitF,ExecF,FiniF,Laps, 0). +run_workers_do(InitF,ExecF,FiniF,Laps, Exclude) -> + ?line NumOfProcs = case erlang:system_info(schedulers) of + N when (N > Exclude) -> N - Exclude + end, io:format("smp starting ~p workers\n",[NumOfProcs]), Seeds = [{ProcN,random:uniform(9999)} || ProcN <- lists:seq(1,NumOfProcs)], Parent = self(), - Pids = [spawn_link(fun()-> worker(Seed,InitF,ExecF,FiniF,Laps,Parent,NumOfProcs) end) + Pids = [my_spawn_link(fun()-> worker(Seed,InitF,ExecF,FiniF,Laps,Parent,NumOfProcs) end) || Seed <- Seeds], - wait_pids(Pids). + case Laps of + infinite -> Pids; + _ -> wait_pids(Pids) + end. worker({ProcN,Seed}, InitF, ExecF, FiniF, Laps, Parent, NumOfProcs) -> io:format("smp worker ~p, seed=~p~n",[self(),Seed]), @@ -5463,6 +5516,8 @@ worker_loop(0, _, State) -> State; worker_loop(_, _, [end_of_work|State]) -> State; +worker_loop(infinite, ExecF, State) -> + worker_loop(infinite,ExecF,ExecF(State)); worker_loop(N, ExecF, State) -> worker_loop(N-1,ExecF,ExecF(State)). @@ -5490,47 +5545,47 @@ my_tab_to_list(_Ts,'$end_of_table', Acc) -> lists:reverse(Acc); my_tab_to_list(Ts,Key, Acc) -> my_tab_to_list(Ts,ets:next(Ts,Key),[ets:lookup(Ts, Key)| Acc]). -wait_for_all_schedulers_online_to_execute() -> - PMs = lists:map(fun (Sched) -> - spawn_opt(fun () -> ok end, - [monitor, {scheduler, Sched}]) - end, - lists:seq(1,erlang:system_info(schedulers_online))), - lists:foreach(fun ({P, M}) -> - receive - {'DOWN', M, process, P, _} -> ok - end - end, - PMs), - ok. + +wait_for_memory_deallocations() -> + try + erts_debug:set_internal_state(wait, deallocations) + catch + error:undef -> + erts_debug:set_internal_state(available_internal_state, true), + wait_for_memory_deallocations() + end. + etsmem() -> - %% Wait until it is guaranteed that all already scheduled - %% deallocations of DbTable structures have completed. - wait_for_all_schedulers_online_to_execute(), + wait_for_memory_deallocations(), AllTabs = lists:map(fun(T) -> {T,ets:info(T,name),ets:info(T,size), ets:info(T,memory),ets:info(T,type)} end, ets:all()), + + EtsAllocInfo = erlang:system_info({allocator,ets_alloc}), + ErlangMemoryEts = try erlang:memory(ets) catch error:notsup -> notsup end, + Mem = - {try erlang:memory(ets) catch error:notsup -> notsup end, - case erlang:system_info({allocator,ets_alloc}) of + {ErlangMemoryEts, + case EtsAllocInfo 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), + CS = lists:foldl( + fun ({instance, _, L}, Acc) -> + {value,{_,SBMBCS}} = lists:keysearch(sbmbcs, 1, L), + {value,{_,MBCS}} = lists:keysearch(mbcs, 1, L), + {value,{_,SBCS}} = lists:keysearch(sbcs, 1, L), + [SBMBCS,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, {0,0}, CS) end}, {Mem,AllTabs}. @@ -5590,6 +5645,7 @@ spawn_logger(Procs) -> true -> exit(Proc, kill); _ -> ok end, + erlang:display(process_info(Proc)), receive {'DOWN', Mon, _, _, _} -> ok @@ -5625,7 +5681,7 @@ wait_for_test_procs(Kill) -> ets_test_spawn_logger ! {sync_test_procs, Kill, self()}, receive test_procs_synced -> ok end. -log_test_proc(Proc) -> +log_test_proc(Proc) when is_pid(Proc) -> ets_test_spawn_logger ! {new_test_proc, Proc}, Proc. @@ -5637,9 +5693,17 @@ my_spawn_link(Fun) -> log_test_proc(spawn_link(Fun)). my_spawn_link(M,F,A) -> log_test_proc(spawn_link(M,F,A)). %%my_spawn_link(N,M,F,A) -> log_test_proc(spawn_link(N,M,F,A)). -my_spawn_opt(Fun,Opts) -> log_test_proc(spawn_opt(Fun,Opts)). -%%my_spawn_opt(M,F,A,Opts) -> log_test_proc(spawn_opt(M,F,A,Opts)). -%%my_spawn_opt(N,M,F,A,Opts) -> log_test_proc(spawn_opt(N,M,F,A,Opts)). +my_spawn_opt(Fun,Opts) -> + case spawn_opt(Fun,Opts) of + Pid when is_pid(Pid) -> log_test_proc(Pid); + {Pid, _} = Res when is_pid(Pid) -> log_test_proc(Pid), Res + end. + +my_spawn_monitor(Fun) -> + Res = spawn_monitor(Fun), + {Pid, _} = Res, + log_test_proc(Pid), + Res. repeat(_Fun, 0) -> ok; @@ -5702,11 +5766,11 @@ spawn_monitor_with_pid(Pid, Fun, N, M) when N > M*10 -> spawn_monitor_with_pid(Pid, Fun, N, M*10); spawn_monitor_with_pid(Pid, Fun, N, M) -> ?line false = is_process_alive(Pid), - case spawn(fun()-> case self() of - Pid -> Fun(); - _ -> die - end - end) of + case my_spawn(fun()-> case self() of + Pid -> Fun(); + _ -> die + end + end) of Pid -> {Pid, erlang:monitor(process, Pid)}; Other -> @@ -5872,7 +5936,7 @@ very_big_num(0, Result) -> ?line Result. make_port() -> - ?line open_port({spawn, efile}, [eof]). + ?line open_port({spawn, "efile"}, [eof]). make_pid() -> ?line spawn_link(?MODULE, sleeper, []). diff --git a/lib/stdlib/test/file_sorter_SUITE.erl b/lib/stdlib/test/file_sorter_SUITE.erl index 80d4ea5fdc..74c08912be 100644 --- a/lib/stdlib/test/file_sorter_SUITE.erl +++ b/lib/stdlib/test/file_sorter_SUITE.erl @@ -89,7 +89,7 @@ basic(suite) -> basic(Config) when is_list(Config) -> Fmt = binary, Arg = {format,Fmt}, - Foo = outfile(foo, Config), + Foo = outfile("foo", Config), P0 = pps(), ?line F1s = [F1] = to_files([[]], Fmt, Config), @@ -455,7 +455,7 @@ inout(suite) -> []; inout(Config) when is_list(Config) -> BTF = {format, binary_term}, - Foo = outfile(foo, Config), + Foo = outfile("foo", Config), %% Input is fun. End = fun(read) -> end_of_input end, @@ -522,7 +522,7 @@ many(doc) -> many(suite) -> []; many(Config) when is_list(Config) -> - Foo = outfile(foo, Config), + Foo = outfile("foo", Config), PrivDir = ?privdir(Config), P0 = pps(), @@ -587,7 +587,7 @@ misc(suite) -> []; misc(Config) when is_list(Config) -> BTF = {format, binary_term}, - Foo = outfile(foo, Config), + Foo = outfile("foo", Config), FFoo = filename:absname(Foo), P0 = pps(), @@ -704,7 +704,7 @@ misc(Config) when is_list(Config) -> sort(Fmt, XArgs, Config) -> Args = make_args(Fmt, [{size,5} | XArgs]), TmpArgs = [{tmpdir,?privdir(Config)} | Args], - Foo = outfile(foo, Config), + Foo = outfile("foo", Config), %% Input is a fun. Output is a fun. ?line [] = file_sorter:sort(input([], 2, Fmt), output([], Fmt), Args), @@ -777,7 +777,7 @@ sort(Fmt, XArgs, Config) -> keysort(Fmt, XArgs, Config) -> Args = make_args(Fmt, [{size,50}, {no_files, 2} | XArgs]), TmpArgs = Args ++ [{tmpdir,?privdir(Config)}], - Foo = outfile(foo, Config), + Foo = outfile("foo", Config), %% Input is files. Output is a file. ?line ok = file_sorter:keysort(2, [], Foo, Args), @@ -836,7 +836,7 @@ keysort(Fmt, XArgs, Config) -> merge(Fmt, XArgs, Config) -> Args = make_args(Fmt, [{size,5} | XArgs]), - Foo = outfile(foo, Config), + Foo = outfile("foo", Config), %% Input is a file. Output is a fun. ?line [] = file_sorter:merge([], output([], Fmt), Args), @@ -873,7 +873,7 @@ merge(Fmt, XArgs, Config) -> keymerge(Fmt, XArgs, Config) -> Args = make_args(Fmt, [{size,50}, {no_files, 2} | XArgs]), - Foo = outfile(foo, Config), + Foo = outfile("foo", Config), %% Input is files. Output is a file. ?line ok = file_sorter:keymerge(2, [], Foo, Args), diff --git a/lib/stdlib/test/filelib_SUITE.erl b/lib/stdlib/test/filelib_SUITE.erl index a355097fe2..1de639a166 100644 --- a/lib/stdlib/test/filelib_SUITE.erl +++ b/lib/stdlib/test/filelib_SUITE.erl @@ -97,11 +97,12 @@ wildcard_errors(Config) when is_list(Config) -> wcc(Wc, Error) -> {'EXIT',{{badpattern,Error}, - [{filelib,compile_wildcard,1}|_]}} = (catch filelib:compile_wildcard(Wc)), + [{filelib,compile_wildcard,1,_}|_]}} = + (catch filelib:compile_wildcard(Wc)), {'EXIT',{{badpattern,Error}, - [{filelib,wildcard,1}|_]}} = (catch filelib:wildcard(Wc)), + [{filelib,wildcard,1,_}|_]}} = (catch filelib:wildcard(Wc)), {'EXIT',{{badpattern,Error}, - [{filelib,wildcard,2}|_]}} = (catch filelib:wildcard(Wc, ".")). + [{filelib,wildcard,2,_}|_]}} = (catch filelib:wildcard(Wc, ".")). do_wildcard_1(Dir, Wcf0) -> do_wildcard_2(Dir, Wcf0), @@ -243,7 +244,7 @@ otp_5960(doc) -> ["Test that filelib:ensure_dir/1 returns ok or {error,Reason}"]; otp_5960(Config) when is_list(Config) -> ?line PrivDir = ?config(priv_dir, Config), - ?line Dir = filename:join(PrivDir, otp_5960_dir), + ?line Dir = filename:join(PrivDir, "otp_5960_dir"), ?line Name1 = filename:join(Dir, name1), ?line Name2 = filename:join(Dir, name2), ?line ok = filelib:ensure_dir(Name1), % parent is created @@ -268,7 +269,7 @@ otp_5960(Config) when is_list(Config) -> ensure_dir_eexist(Config) when is_list(Config) -> ?line PrivDir = ?config(priv_dir, Config), - ?line Dir = filename:join(PrivDir, ensure_dir_eexist), + ?line Dir = filename:join(PrivDir, "ensure_dir_eexist"), ?line Name = filename:join(Dir, "same_name_as_file_and_dir"), ?line ok = filelib:ensure_dir(Name), ?line ok = file:write_file(Name, <<"some string\n">>), diff --git a/lib/stdlib/test/gen_server_SUITE.erl b/lib/stdlib/test/gen_server_SUITE.erl index a614d6595d..7fb8d54f2d 100644 --- a/lib/stdlib/test/gen_server_SUITE.erl +++ b/lib/stdlib/test/gen_server_SUITE.erl @@ -694,7 +694,7 @@ multicall_down(Config) when is_list(Config) -> %% We use 'global' as a gen_server to call. ?line {Good, Bad} = gen_server:multi_call([Name, node()], global_name_server, - {whereis, gurkburk}, + info, 3000), io:format("good = ~p, bad = ~p~n", [Good, Bad]), ?line [Name] = Bad, diff --git a/lib/stdlib/test/io_SUITE.erl b/lib/stdlib/test/io_SUITE.erl index 54a98985cd..bb02a879c2 100644 --- a/lib/stdlib/test/io_SUITE.erl +++ b/lib/stdlib/test/io_SUITE.erl @@ -27,7 +27,7 @@ otp_6282/1, otp_6354/1, otp_6495/1, otp_6517/1, otp_6502/1, manpage/1, otp_6708/1, otp_7084/1, otp_7421/1, io_lib_collect_line_3_wb/1, cr_whitespace_in_string/1, - io_fread_newlines/1, otp_8989/1]). + io_fread_newlines/1, otp_8989/1, io_lib_fread_literal/1]). %-define(debug, true). @@ -62,7 +62,7 @@ all() -> otp_6282, otp_6354, otp_6495, otp_6517, otp_6502, manpage, otp_6708, otp_7084, otp_7421, io_lib_collect_line_3_wb, cr_whitespace_in_string, - io_fread_newlines, otp_8989]. + io_fread_newlines, otp_8989, io_lib_fread_literal]. groups() -> []. @@ -1995,3 +1995,29 @@ otp_8989(Suite) when is_list(Suite) -> ?line "Hel " = fmt("~-4.*s", [3,Hello]), ?line "Hel " = fmt("~*.*s", [-4,3,Hello]), ok. + +io_lib_fread_literal(doc) -> + "OTP-9439 io_lib:fread bug for literate at end"; +io_lib_fread_literal(Suite) when is_list(Suite) -> + ?line {more,"~d",0,""} = io_lib:fread("~d", ""), + ?line {error,{fread,integer}} = io_lib:fread("~d", " "), + ?line {more,"~d",1,""} = io_lib:fread(" ~d", " "), + ?line {ok,[17],"X"} = io_lib:fread(" ~d", " 17X"), + %% + ?line {more,"d",0,""} = io_lib:fread("d", ""), + ?line {error,{fread,input}} = io_lib:fread("d", " "), + ?line {more,"d",1,""} = io_lib:fread(" d", " "), + ?line {ok,[],"X"} = io_lib:fread(" d", " dX"), + %% + ?line {done,eof,_} = io_lib:fread([], eof, "~d"), + ?line {done,eof,_} = io_lib:fread([], eof, " ~d"), + ?line {more,C1} = io_lib:fread([], " \n", " ~d"), + ?line {done,{error,{fread,input}},_} = io_lib:fread(C1, eof, " ~d"), + ?line {done,{ok,[18]},""} = io_lib:fread(C1, "18\n", " ~d"), + %% + ?line {done,eof,_} = io_lib:fread([], eof, "d"), + ?line {done,eof,_} = io_lib:fread([], eof, " d"), + ?line {more,C2} = io_lib:fread([], " \n", " d"), + ?line {done,{error,{fread,input}},_} = io_lib:fread(C2, eof, " d"), + ?line {done,{ok,[]},[]} = io_lib:fread(C2, "d\n", " d"), + ok. diff --git a/lib/stdlib/test/ms_transform_SUITE.erl b/lib/stdlib/test/ms_transform_SUITE.erl index 4e5df12798..c9688354b1 100644 --- a/lib/stdlib/test/ms_transform_SUITE.erl +++ b/lib/stdlib/test/ms_transform_SUITE.erl @@ -39,6 +39,7 @@ -export([float_1_function/1]). -export([action_function/1]). -export([warnings/1]). +-export([no_warnings/1]). -export([init_per_testcase/2, end_per_testcase/2]). init_per_testcase(_Func, Config) -> @@ -55,7 +56,7 @@ all() -> [from_shell, basic_ets, basic_dbg, records, record_index, multipass, bitsyntax, record_defaults, andalso_orelse, float_1_function, action_function, - warnings, top_match, old_guards, autoimported, + warnings, no_warnings, top_match, old_guards, autoimported, semicolon]. groups() -> @@ -155,6 +156,34 @@ warnings(Config) when is_list(Config) -> compile_ww(Prog7), ok. +no_warnings(suite) -> + []; +no_warnings(doc) -> + ["Check that variables bound in other function clauses don't generate " + "warning"]; +no_warnings(Config) when is_list(Config) -> + ?line setup(Config), + Prog = <<"tmp(X) when X > 100 ->\n", + " Y=X,\n" + " Y;\n" + "tmp(X) ->\n" + " ets:fun2ms(fun(Y) ->\n" + " {X, 3*Y}\n" + " end)">>, + ?line [] = compile_no_ww(Prog), + + Prog2 = <<"tmp(X) when X > 100 ->\n", + " Y=X,\n" + " Y;\n" + "tmp(X) when X < 200 ->\n" + " ok;\n" + "tmp(X) ->\n" + " ets:fun2ms(fun(Y) ->\n" + " {X, 3*Y}\n" + " end)">>, + ?line [] = compile_no_ww(Prog2), + ok. + andalso_orelse(suite) -> []; andalso_orelse(doc) -> @@ -842,6 +871,20 @@ compile_ww(Records,Expr) -> nowarn_unused_record]), Wlist. +compile_no_ww(Expr) -> + Prog = << + "-module(tmp).\n", + "-include_lib(\"stdlib/include/ms_transform.hrl\").\n", + "-export([tmp/1]).\n\n", + Expr/binary,".\n">>, + FN=temp_name(), + file:write_file(FN,Prog), + {ok,Forms} = epp:parse_file(FN,"",""), + {ok,tmp,_Bin,Wlist} = compile:forms(Forms,[return_warnings, + nowarn_unused_vars, + nowarn_unused_record]), + Wlist. + do_eval(String) -> {done,{ok,T,_},[]} = erl_scan:tokens( [], diff --git a/lib/stdlib/test/proc_lib_SUITE.erl b/lib/stdlib/test/proc_lib_SUITE.erl index 1565aa9bba..c95089117c 100644 --- a/lib/stdlib/test/proc_lib_SUITE.erl +++ b/lib/stdlib/test/proc_lib_SUITE.erl @@ -328,7 +328,7 @@ otp_6345(doc) -> ["'monitor' spawn_opt option"]; otp_6345(Config) when is_list(Config) -> Opts = [link,monitor], - {'EXIT', {badarg,[{proc_lib,check_for_monitor,_}|_Stack]}} = + {'EXIT', {badarg,[{proc_lib,check_for_monitor,_,_}|_Stack]}} = (catch proc_lib:start(?MODULE, otp_6345_init, [self()], 1000, Opts)), ok. diff --git a/lib/stdlib/test/qlc_SUITE.erl b/lib/stdlib/test/qlc_SUITE.erl index 98eeaee118..8a9d8f7883 100644 --- a/lib/stdlib/test/qlc_SUITE.erl +++ b/lib/stdlib/test/qlc_SUITE.erl @@ -6632,7 +6632,7 @@ otp_7232(Config) when is_list(Config) -> {call,_, {remote,_,{atom,_,qlc},{atom,_,sort}}, [{cons,_, - {'fun',_,{function,math,sqrt,_}}, + {'fun',_,{function,{atom,_,math},{atom,_,sqrt},_}}, {cons,_, {string,_,\"<0.4.1>\"}, % could use list_to_pid.. {cons,_,{string,_,\"#Ref<\"++_},{nil,_}}}}, diff --git a/lib/stdlib/test/re_SUITE.erl b/lib/stdlib/test/re_SUITE.erl index c4817c0d38..3b2e637c84 100644 --- a/lib/stdlib/test/re_SUITE.erl +++ b/lib/stdlib/test/re_SUITE.erl @@ -454,115 +454,115 @@ error_handling(Config) when is_list(Config) -> % The malformed precomiled RE is detected after % the trap to re:grun from grun, in the grun function clause % that handles precompiled expressions - ?line {'EXIT',{badarg,[{re,run,["apa",{1,2,3,4},[global]]}, - {?MODULE, error_handling,1} | _]}} = + ?line {'EXIT',{badarg,[{re,run,["apa",{1,2,3,4},[global]],_}, + {?MODULE,error_handling,1,_} | _]}} = (catch re:run("apa",{1,2,3,4},[global])), % An invalid capture list will also cause a badarg late, % but with a non pre compiled RE, the exception should be thrown by the % grun function clause that handles RE's compiled implicitly by % the run/3 BIF before trapping. - ?line {'EXIT',{badarg,[{re,run,["apa","p",[{capture,[1,{a}]},global]]}, - {?MODULE, error_handling,1} | _]}} = + ?line {'EXIT',{badarg,[{re,run,["apa","p",[{capture,[1,{a}]},global]],_}, + {?MODULE,error_handling,1,_} | _]}} = (catch re:run("apa","p",[{capture,[1,{a}]},global])), % And so the case of a precompiled expression together with % a compile-option (binary and list subject): ?line {ok,RE} = re:compile("(p)"), ?line {match,[[{1,1},{1,1}]]} = re:run(<<"apa">>,RE,[global]), ?line {match,[[{1,1},{1,1}]]} = re:run("apa",RE,[global]), - {'EXIT',{badarg,[{re,run, - [<<"apa">>, - {re_pattern,1,0,_}, - [global,unicode]]}, - {?MODULE, error_handling,1} | _]}} = + ?line {'EXIT',{badarg,[{re,run, + [<<"apa">>, + {re_pattern,1,0,_}, + [global,unicode]],_}, + {?MODULE,error_handling,1,_} | _]}} = (catch re:run(<<"apa">>,RE,[global,unicode])), - {'EXIT',{badarg,[{re,run, - ["apa", - {re_pattern,1,0,_}, - [global,unicode]]}, - {?MODULE, error_handling,1} | _]}} = + ?line {'EXIT',{badarg,[{re,run, + ["apa", + {re_pattern,1,0,_}, + [global,unicode]],_}, + {?MODULE,error_handling,1,_} | _]}} = (catch re:run("apa",RE,[global,unicode])), ?line {'EXIT',{badarg,_}} = (catch re:run("apa","(p",[])), ?line {'EXIT',{badarg,_}} = (catch re:run("apa","(p",[global])), % The replace errors: - ?line {'EXIT',{badarg,[{re,replace,["apa",{1,2,3,4},"X",[]]}, - {?MODULE, error_handling,1} | _]}} = + ?line {'EXIT',{badarg,[{re,replace,["apa",{1,2,3,4},"X",[]],_}, + {?MODULE,error_handling,1,_} | _]}} = (catch re:replace("apa",{1,2,3,4},"X",[])), - ?line {'EXIT',{badarg,[{re,replace,["apa",{1,2,3,4},"X",[global]]}, - {?MODULE, error_handling,1} | _]}} = + ?line {'EXIT',{badarg,[{re,replace,["apa",{1,2,3,4},"X",[global]],_}, + {?MODULE,error_handling,1,_} | _]}} = (catch re:replace("apa",{1,2,3,4},"X",[global])), ?line {'EXIT',{badarg,[{re,replace, ["apa", {re_pattern,1,0,_}, "X", - [unicode]]}, - {?MODULE, error_handling,1} | _]}} = + [unicode]],_}, + {?MODULE,error_handling,1,_} | _]}} = (catch re:replace("apa",RE,"X",[unicode])), ?line <<"aXa">> = iolist_to_binary(re:replace("apa","p","X",[])), ?line {'EXIT',{badarg,[{re,replace, - ["apa","p","X",[{capture,all,binary}]]}, - {?MODULE, error_handling,1} | _]}} = + ["apa","p","X",[{capture,all,binary}]],_}, + {?MODULE,error_handling,1,_} | _]}} = (catch iolist_to_binary(re:replace("apa","p","X", [{capture,all,binary}]))), ?line {'EXIT',{badarg,[{re,replace, - ["apa","p","X",[{capture,all}]]}, - {?MODULE, error_handling,1} | _]}} = + ["apa","p","X",[{capture,all}]],_}, + {?MODULE,error_handling,1,_} | _]}} = (catch iolist_to_binary(re:replace("apa","p","X", [{capture,all}]))), ?line {'EXIT',{badarg,[{re,replace, - ["apa","p","X",[{return,banana}]]}, - {?MODULE, error_handling,1} | _]}} = + ["apa","p","X",[{return,banana}]],_}, + {?MODULE,error_handling,1,_} | _]}} = (catch iolist_to_binary(re:replace("apa","p","X", [{return,banana}]))), ?line {'EXIT',{badarg,_}} = (catch re:replace("apa","(p","X",[])), % Badarg, not compile error. ?line {'EXIT',{badarg,[{re,replace, - ["apa","(p","X",[{return,banana}]]}, - {?MODULE, error_handling,1} | _]}} = + ["apa","(p","X",[{return,banana}]],_}, + {?MODULE,error_handling,1,_} | _]}} = (catch iolist_to_binary(re:replace("apa","(p","X", [{return,banana}]))), % And the split errors: ?line [<<"a">>,<<"a">>] = (catch re:split("apa","p",[])), ?line [<<"a">>,<<"p">>,<<"a">>] = (catch re:split("apa",RE,[])), - ?line {'EXIT',{badarg,[{re,split,["apa","p",[global]]}, - {?MODULE, error_handling,1} | _]}} = + ?line {'EXIT',{badarg,[{re,split,["apa","p",[global]],_}, + {?MODULE,error_handling,1,_} | _]}} = (catch re:split("apa","p",[global])), - ?line {'EXIT',{badarg,[{re,split,["apa","p",[{capture,all}]]}, - {?MODULE, error_handling,1} | _]}} = + ?line {'EXIT',{badarg,[{re,split,["apa","p",[{capture,all}]],_}, + {?MODULE,error_handling,1,_} | _]}} = (catch re:split("apa","p",[{capture,all}])), - ?line {'EXIT',{badarg,[{re,split,["apa","p",[{capture,all,binary}]]}, - {?MODULE, error_handling,1} | _]}} = + ?line {'EXIT',{badarg,[{re,split,["apa","p",[{capture,all,binary}]],_}, + {?MODULE, error_handling,1,_} | _]}} = (catch re:split("apa","p",[{capture,all,binary}])), - ?line {'EXIT',{badarg,[{re,split,["apa",{1,2,3,4},[]]}, - {?MODULE, error_handling,1} | _]}} = + ?line {'EXIT',{badarg,[{re,split,["apa",{1,2,3,4},[]],_}, + {?MODULE,error_handling,1,_} | _]}} = (catch re:split("apa",{1,2,3,4})), - ?line {'EXIT',{badarg,[{re,split,["apa",{1,2,3,4},[]]}, - {?MODULE, error_handling,1} | _]}} = + ?line {'EXIT',{badarg,[{re,split,["apa",{1,2,3,4},[]],_}, + {?MODULE,error_handling,1,_} | _]}} = (catch re:split("apa",{1,2,3,4},[])), ?line {'EXIT',{badarg,[{re,split, ["apa", RE, - [unicode]]}, - {?MODULE, error_handling,1} | _]}} = + [unicode]],_}, + {?MODULE,error_handling,1,_} | _]}} = (catch re:split("apa",RE,[unicode])), ?line {'EXIT',{badarg,[{re,split, ["apa", RE, - [{return,banana}]]}, - {?MODULE, error_handling,1} | _]}} = + [{return,banana}]],_}, + {?MODULE,error_handling,1,_} | _]}} = (catch re:split("apa",RE,[{return,banana}])), ?line {'EXIT',{badarg,[{re,split, ["apa", RE, - [banana]]}, - {?MODULE, error_handling,1} | _]}} = + [banana]],_}, + {?MODULE,error_handling,1,_} | _]}} = (catch re:split("apa",RE,[banana])), ?line {'EXIT',{badarg,_}} = (catch re:split("apa","(p")), %Exception on bad argument, not compilation error ?line {'EXIT',{badarg,[{re,split, ["apa", "(p", - [banana]]}, - {?MODULE, error_handling,1} | _]}} = + [banana]],_}, + {?MODULE,error_handling,1,_} | _]}} = (catch re:split("apa","(p",[banana])), ?t:timetrap_cancel(Dog), ok. diff --git a/lib/stdlib/test/shell_SUITE.erl b/lib/stdlib/test/shell_SUITE.erl index 8273377ba1..b6019b86f0 100644 --- a/lib/stdlib/test/shell_SUITE.erl +++ b/lib/stdlib/test/shell_SUITE.erl @@ -2388,12 +2388,12 @@ otp_6554(Config) when is_list(Config) -> comm_err(<<"V = lists:seq(1, 20), case V of a -> ok end.">>), ?line "exception error: no function clause matching" = comm_err(<<"fun(P) when is_pid(P) -> true end(a).">>), - ?line "exception error: {function_clause,[{erl_eval,do_apply,[unproper|list]}"++_ = + ?line "exception error: {function_clause," = comm_err(<<"erlang:error(function_clause, [unproper | list]).">>), ?line "exception error: function_clause" = comm_err(<<"erlang:error(function_clause, 4).">>), %% Cheating: - ?line "exception error: no function clause matching erl_eval:do_apply(4)" = + ?line "exception error: no function clause matching erl_eval:do_apply(4)" ++ _ = comm_err(<<"erlang:error(function_clause, [4]).">>), ?line "exception error: no function clause matching" ++ _ = comm_err(<<"fun(a, b, c, d) -> foo end" @@ -2406,7 +2406,7 @@ otp_6554(Config) when is_list(Config) -> comm_err(<<"fun(P, q) when is_pid(P) -> true end(a, b).">>), ?line "exception error: no function clause matching lists:reverse(" ++ _ = comm_err(<<"F=fun() -> hello end, lists:reverse(F).">>), - ?line "exception error: no function clause matching lists:reverse(34)" = + ?line "exception error: no function clause matching lists:reverse(34) (lists.erl, line " ++ _ = comm_err(<<"lists:reverse(34).">>), ?line "exception error: no true branch found when evaluating an if expression" = comm_err(<<"if length([a,b]) > 17 -> a end.">>), diff --git a/lib/stdlib/test/sofs_SUITE.erl b/lib/stdlib/test/sofs_SUITE.erl index 01de1f0600..73b282149a 100644 --- a/lib/stdlib/test/sofs_SUITE.erl +++ b/lib/stdlib/test/sofs_SUITE.erl @@ -1602,19 +1602,15 @@ relative_product_2(Conf) when is_list(Conf) -> from_term([{{a},b}])}, ER)), ?line {'EXIT', {badarg, _}} = (catch relative_product({}, ER)), - ?line eval(relative_product({relation([{a,b}])}, - from_term([],[{{atom},atom}])), - ER), - ?line eval(relative_product({relation([{a,b}]),relation([{a,1}])}, - from_term([{{b,1},{tjo,hej,sa}}])), - from_term([{a,{tjo,hej,sa}}])), - ?line eval(relative_product({relation([{a,b}]), ER}, - from_term([{{a,b},b}])), - ER), - ?line eval(relative_product({relation([{a,b},{c,a}]), - relation([{a,1},{a,2}])}, - from_term([{{b,1},b1},{{b,2},b2}])), - relation([{a,b1},{a,b2}])), + ?line relprod2({relation([{a,b}])}, from_term([],[{{atom},atom}]), ER), + ?line relprod2({relation([{a,b}]),relation([{a,1}])}, + from_term([{{b,1},{tjo,hej,sa}}]), + from_term([{a,{tjo,hej,sa}}])), + ?line relprod2({relation([{a,b}]), ER}, from_term([{{a,b},b}]), ER), + ?line relprod2({relation([{a,b},{c,a}]), + relation([{a,1},{a,2}])}, + from_term([{{b,1},b1},{{b,2},b2}]), + relation([{a,b1},{a,b2}])), ?line eval(relative_product({relation([{a,b}]), ER}), from_term([],[{atom,{atom,atom}}])), ?line eval(relative_product({from_term([{{a,[a,b]},[a]}]), @@ -1622,6 +1618,11 @@ relative_product_2(Conf) when is_list(Conf) -> from_term([{{a,[a,b]},{[a],[[a,b]]}}])), ok. +relprod2(A1T, A2, R) -> + %% A tuple as first argument is the old interface: + eval(relative_product(A1T, A2), R), + eval(relative_product(tuple_to_list(A1T), A2), R). + product_1(suite) -> []; product_1(doc) -> [""]; product_1(Conf) when is_list(Conf) -> @@ -1878,11 +1879,11 @@ digraph(Conf) when is_list(Conf) -> ?line {'EXIT', {badarg, _}} = (catch family_to_digraph(set([a]))), - ?line {'EXIT', {badarg, [{sofs,family_to_digraph,[_,_]}|_]}} = + ?line {'EXIT', {badarg, [{sofs,family_to_digraph,[_,_],_}|_]}} = (catch family_to_digraph(set([a]), [foo])), - ?line {'EXIT', {badarg, [{sofs,family_to_digraph,[_,_]}|_]}} = + ?line {'EXIT', {badarg, [{sofs,family_to_digraph,[_,_],_}|_]}} = (catch family_to_digraph(F, [foo])), - ?line {'EXIT', {cyclic, [{sofs,family_to_digraph,[_,_]}|_]}} = + ?line {'EXIT', {cyclic, [{sofs,family_to_digraph,[_,_],_}|_]}} = (catch family_to_digraph(family([{a,[a]}]),[acyclic])), ?line G1 = family_to_digraph(E), diff --git a/lib/stdlib/test/string_SUITE.erl b/lib/stdlib/test/string_SUITE.erl index 1dcd4be21e..6969c095a0 100644 --- a/lib/stdlib/test/string_SUITE.erl +++ b/lib/stdlib/test/string_SUITE.erl @@ -273,9 +273,9 @@ words(Config) when is_list(Config) -> ?line 2 = string:words("2.35", $.), ?line 100 = string:words(string:copies(". ", 100)), %% invalid arg type - ?line {'EXIT',_} = (catch string:chars(hej)), + ?line {'EXIT',_} = (catch string:chars(hej, 1)), %% invalid arg type - ?line {'EXIT',_} = (catch string:chars("hej", " ")), + ?line {'EXIT',_} = (catch string:chars("hej", 1, " ")), ok. diff --git a/lib/stdlib/test/supervisor_1.erl b/lib/stdlib/test/supervisor_1.erl index 3198be0fed..f819594c46 100644 --- a/lib/stdlib/test/supervisor_1.erl +++ b/lib/stdlib/test/supervisor_1.erl @@ -62,6 +62,12 @@ handle_info(die, State) -> handle_info(stop, State) -> {stop, normal, State}; +handle_info({'EXIT',_,shutdown}, State) -> + {stop, shutdown, State}; + +handle_info({'EXIT',_,{shutdown,Term}}, State) -> + {stop, {shutdown,Term}, State}; + handle_info({sleep, Time}, State) -> io:format("FOO: ~p~n", [Time]), timer:sleep(Time), diff --git a/lib/stdlib/test/supervisor_2.erl b/lib/stdlib/test/supervisor_2.erl new file mode 100644 index 0000000000..67aacf5a9c --- /dev/null +++ b/lib/stdlib/test/supervisor_2.erl @@ -0,0 +1,42 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1996-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% +%% +%% Description: Simulates the behaviour that a child process may have. +%% Is used by the supervisor_SUITE test suite. +-module(supervisor_2). + +-export([start_child/1, init/1]). + +-export([handle_call/3, handle_info/2, terminate/2]). + +start_child(Time) when is_integer(Time), Time > 0 -> + gen_server:start_link(?MODULE, Time, []). + +init(Time) -> + process_flag(trap_exit, true), + {ok, Time}. + +handle_call(Req, _From, State) -> + {reply, Req, State}. + +handle_info(_, State) -> + {noreply, State}. + +terminate(_Reason, Time) -> + timer:sleep(Time), + ok. diff --git a/lib/stdlib/test/supervisor_SUITE.erl b/lib/stdlib/test/supervisor_SUITE.erl index f9ceed8f84..d3d140abbc 100644 --- a/lib/stdlib/test/supervisor_SUITE.erl +++ b/lib/stdlib/test/supervisor_SUITE.erl @@ -20,7 +20,7 @@ -module(supervisor_SUITE). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -define(TIMEOUT, 1000). %% Testserver specific export @@ -28,8 +28,9 @@ init_per_group/2,end_per_group/2, init_per_testcase/2, end_per_testcase/2]). -%% Indirect spawn export --export([init/1]). +%% Internal export +-export([init/1, terminate_all_children/1, + middle9212/0, gen_server9212/0, handle_info/2]). %% API tests -export([ sup_start_normal/1, sup_start_ignore_init/1, @@ -41,8 +42,10 @@ %% Tests concept permanent, transient and temporary -export([ permanent_normal/1, transient_normal/1, temporary_normal/1, + permanent_shutdown/1, transient_shutdown/1, + temporary_shutdown/1, permanent_abnormal/1, transient_abnormal/1, - temporary_abnormal/1]). + temporary_abnormal/1, temporary_bystander/1]). %% Restart strategy tests -export([ one_for_one/1, @@ -50,12 +53,14 @@ one_for_all_escalation/1, simple_one_for_one/1, simple_one_for_one_escalation/1, rest_for_one/1, rest_for_one_escalation/1, - simple_one_for_one_extra/1]). + simple_one_for_one_extra/1, simple_one_for_one_shutdown/1]). %% Misc tests -export([child_unlink/1, tree/1, count_children_memory/1, do_not_save_start_parameters_for_temporary_children/1, - do_not_save_child_specs_for_temporary_children/1]). + do_not_save_child_specs_for_temporary_children/1, + simple_one_for_one_scale_many_temporary_children/1, + simple_global_supervisor/1]). %%------------------------------------------------------------------------- @@ -70,9 +75,12 @@ all() -> {group, restart_simple_one_for_one}, {group, restart_rest_for_one}, {group, normal_termination}, + {group, shutdown_termination}, {group, abnormal_termination}, child_unlink, tree, count_children_memory, do_not_save_start_parameters_for_temporary_children, - do_not_save_child_specs_for_temporary_children]. + do_not_save_child_specs_for_temporary_children, + simple_one_for_one_scale_many_temporary_children, temporary_bystander, + simple_global_supervisor]. groups() -> [{sup_start, [], @@ -84,6 +92,8 @@ groups() -> sup_stop_brutal_kill]}, {normal_termination, [], [permanent_normal, transient_normal, temporary_normal]}, + {shutdown_termination, [], + [permanent_shutdown, transient_shutdown, temporary_shutdown]}, {abnormal_termination, [], [permanent_abnormal, transient_abnormal, temporary_abnormal]}, @@ -92,8 +102,8 @@ groups() -> {restart_one_for_all, [], [one_for_all, one_for_all_escalation]}, {restart_simple_one_for_one, [], - [simple_one_for_one, simple_one_for_one_extra, - simple_one_for_one_escalation]}, + [simple_one_for_one, simple_one_for_one_shutdown, + simple_one_for_one_extra, simple_one_for_one_escalation]}, {restart_rest_for_one, [], [rest_for_one, rest_for_one_escalation]}]. @@ -112,16 +122,20 @@ end_per_group(_GroupName, Config) -> Config. init_per_testcase(count_children_memory, Config) -> - MemoryState = erlang:system_info(allocator), - case count_children_allocator_test(MemoryState) of - true -> Config; - false -> + try erlang:memory() of + _ -> + erts_debug:set_internal_state(available_internal_state, true), + Config + catch error:notsup -> {skip, "+Meamin used during test; erlang:memory/1 not available"} end; init_per_testcase(_Case, Config) -> erlang:display(_Case), Config. +end_per_testcase(count_children_memory, _Config) -> + catch erts_debug:set_internal_state(available_internal_state, false), + ok; end_per_testcase(_Case, _Config) -> ok. @@ -203,8 +217,8 @@ sup_start_fail(Config) when is_list(Config) -> %%------------------------------------------------------------------------- sup_stop_infinity(doc) -> - ["See sup_stop/1 when Shutdown = infinity, this walue is only allowed " - "for children of type supervisor"]; + ["See sup_stop/1 when Shutdown = infinity, this walue is allowed " + "for children of type supervisor _AND_ worker"]; sup_stop_infinity(suite) -> []; sup_stop_infinity(Config) when is_list(Config) -> @@ -215,12 +229,13 @@ sup_stop_infinity(Config) when is_list(Config) -> Child2 = {child2, {supervisor_1, start_child, []}, permanent, infinity, worker, []}, {ok, CPid1} = supervisor:start_child(sup_test, Child1), + {ok, CPid2} = supervisor:start_child(sup_test, Child2), link(CPid1), - {error, {invalid_shutdown,infinity}} = - supervisor:start_child(sup_test, Child2), + link(CPid2), terminate(Pid, shutdown), - check_exit_reason(CPid1, shutdown). + check_exit_reason(CPid1, shutdown), + check_exit_reason(CPid2, shutdown). %%------------------------------------------------------------------------- @@ -349,8 +364,7 @@ child_adm(Config) when is_list(Config) -> ok = supervisor:terminate_child(sup_test, child1), %% Start of already existing but not running process - {error,already_present} = - supervisor:start_child(sup_test, Child), + {error,already_present} = supervisor:start_child(sup_test, Child), %% Restart {ok, CPid2} = supervisor:restart_child(sup_test, child1), @@ -377,6 +391,11 @@ child_adm(Config) when is_list(Config) -> [{child1, CPid3, worker, []}] = supervisor:which_children(sup_test), [1,1,0,1] = get_child_counts(sup_test), + %% Terminate with Pid not allowed when not simple_one_for_one + {error,not_found} = supervisor:terminate_child(sup_test, CPid3), + [{child1, CPid3, worker, []}] = supervisor:which_children(sup_test), + [1,1,0,1] = get_child_counts(sup_test), + {'EXIT',{noproc,{gen_server,call,[foo,which_children,infinity]}}} = (catch supervisor:which_children(foo)), {'EXIT',{noproc,{gen_server,call,[foo,count_children,infinity]}}} @@ -412,16 +431,26 @@ child_adm_simple(Config) when is_list(Config) -> [1,2,0,2] = get_child_counts(sup_test), %% Termination - {error, simple_one_for_one} = - supervisor:terminate_child(sup_test, child1), + {error, simple_one_for_one} = supervisor:terminate_child(sup_test, child1), + [1,2,0,2] = get_child_counts(sup_test), + ok = supervisor:terminate_child(sup_test,CPid1), + [_] = supervisor:which_children(sup_test), + [1,1,0,1] = get_child_counts(sup_test), + false = erlang:is_process_alive(CPid1), + %% Terminate non-existing proccess is ok + ok = supervisor:terminate_child(sup_test,CPid1), + [_] = supervisor:which_children(sup_test), + [1,1,0,1] = get_child_counts(sup_test), + %% Terminate pid which is not a child of this supervisor is not ok + NoChildPid = spawn_link(fun() -> receive after infinity -> ok end end), + {error, not_found} = supervisor:terminate_child(sup_test, NoChildPid), + true = erlang:is_process_alive(NoChildPid), %% Restart - {error, simple_one_for_one} = - supervisor:restart_child(sup_test, child1), + {error, simple_one_for_one} = supervisor:restart_child(sup_test, child1), %% Deletion - {error, simple_one_for_one} = - supervisor:delete_child(sup_test, child1), + {error, simple_one_for_one} = supervisor:delete_child(sup_test, child1), ok. %%------------------------------------------------------------------------- @@ -438,9 +467,8 @@ child_specs(Config) when is_list(Config) -> B2 = {child, {m,f,[a]}, prmanent, 1000, worker, []}, B3 = {child, {m,f,[a]}, permanent, -10, worker, []}, B4 = {child, {m,f,[a]}, permanent, 10, wrker, []}, - B5 = {child, {m,f,[a]}, permanent, infinity, worker, []}, - B6 = {child, {m,f,[a]}, permanent, 1000, worker, dy}, - B7 = {child, {m,f,[a]}, permanent, 1000, worker, [1,2,3]}, + B5 = {child, {m,f,[a]}, permanent, 1000, worker, dy}, + B6 = {child, {m,f,[a]}, permanent, 1000, worker, [1,2,3]}, %% Correct child specs! %% <Modules> (last parameter in a child spec) can be [] as we do @@ -449,6 +477,7 @@ child_specs(Config) when is_list(Config) -> C2 = {child, {m,f,[a]}, permanent, 1000, supervisor, []}, C3 = {child, {m,f,[a]}, temporary, 1000, worker, dynamic}, C4 = {child, {m,f,[a]}, transient, 1000, worker, [m]}, + C5 = {child, {m,f,[a]}, permanent, infinity, worker, [m]}, {error, {invalid_mfa,mfa}} = supervisor:start_child(sup_test, B1), {error, {invalid_restart_type, prmanent}} = @@ -457,9 +486,8 @@ child_specs(Config) when is_list(Config) -> = supervisor:start_child(sup_test, B3), {error, {invalid_child_type,wrker}} = supervisor:start_child(sup_test, B4), - {error, _} = supervisor:start_child(sup_test, B5), {error, {invalid_modules,dy}} - = supervisor:start_child(sup_test, B6), + = supervisor:start_child(sup_test, B5), {error, {invalid_mfa,mfa}} = supervisor:check_childspecs([B1]), {error, {invalid_restart_type,prmanent}} = @@ -467,15 +495,15 @@ child_specs(Config) when is_list(Config) -> {error, {invalid_shutdown,-10}} = supervisor:check_childspecs([B3]), {error, {invalid_child_type,wrker}} = supervisor:check_childspecs([B4]), - {error, _} = supervisor:check_childspecs([B5]), - {error, {invalid_modules,dy}} = supervisor:check_childspecs([B6]), + {error, {invalid_modules,dy}} = supervisor:check_childspecs([B5]), {error, {invalid_module, 1}} = - supervisor:check_childspecs([B7]), + supervisor:check_childspecs([B6]), ok = supervisor:check_childspecs([C1]), ok = supervisor:check_childspecs([C2]), ok = supervisor:check_childspecs([C3]), ok = supervisor:check_childspecs([C4]), + ok = supervisor:check_childspecs([C5]), ok. %%------------------------------------------------------------------------- @@ -534,6 +562,87 @@ temporary_normal(Config) when is_list(Config) -> [0,0,0,0] = get_child_counts(sup_test). %%------------------------------------------------------------------------- +permanent_shutdown(doc) -> + ["A permanent child should always be restarted"]; +permanent_shutdown(suite) -> []; +permanent_shutdown(Config) when is_list(Config) -> + {ok, SupPid} = start_link({ok, {{one_for_one, 2, 3600}, []}}), + Child1 = {child1, {supervisor_1, start_child, []}, permanent, 1000, + worker, []}, + + {ok, CPid1} = supervisor:start_child(sup_test, Child1), + + terminate(SupPid, CPid1, child1, shutdown), + + [{child1, CPid2 ,worker,[]}] = supervisor:which_children(sup_test), + case is_pid(CPid2) of + true -> + ok; + false -> + test_server:fail({permanent_child_not_restarted, Child1}) + end, + [1,1,0,1] = get_child_counts(sup_test), + + terminate(SupPid, CPid2, child1, {shutdown, some_info}), + + [{child1, CPid3 ,worker,[]}] = supervisor:which_children(sup_test), + case is_pid(CPid3) of + true -> + ok; + false -> + test_server:fail({permanent_child_not_restarted, Child1}) + end, + + [1,1,0,1] = get_child_counts(sup_test). + +%%------------------------------------------------------------------------- +transient_shutdown(doc) -> + ["A transient child should not be restarted if it exits with " + "reason shutdown or {shutdown,Term}"]; +transient_shutdown(suite) -> []; +transient_shutdown(Config) when is_list(Config) -> + {ok, SupPid} = start_link({ok, {{one_for_one, 2, 3600}, []}}), + Child1 = {child1, {supervisor_1, start_child, []}, transient, 1000, + worker, []}, + + {ok, CPid1} = supervisor:start_child(sup_test, Child1), + + terminate(SupPid, CPid1, child1, shutdown), + + [{child1,undefined,worker,[]}] = supervisor:which_children(sup_test), + [1,0,0,1] = get_child_counts(sup_test), + + {ok, CPid2} = supervisor:restart_child(sup_test, child1), + + terminate(SupPid, CPid2, child1, {shutdown, some_info}), + + [{child1,undefined,worker,[]}] = supervisor:which_children(sup_test), + [1,0,0,1] = get_child_counts(sup_test). + +%%------------------------------------------------------------------------- +temporary_shutdown(doc) -> + ["A temporary process should never be restarted"]; +temporary_shutdown(suite) -> []; +temporary_shutdown(Config) when is_list(Config) -> + {ok, SupPid} = start_link({ok, {{one_for_one, 2, 3600}, []}}), + Child1 = {child1, {supervisor_1, start_child, []}, temporary, 1000, + worker, []}, + + {ok, CPid1} = supervisor:start_child(sup_test, Child1), + + terminate(SupPid, CPid1, child1, shutdown), + + [] = supervisor:which_children(sup_test), + [0,0,0,0] = get_child_counts(sup_test), + + {ok, CPid2} = supervisor:start_child(sup_test, Child1), + + terminate(SupPid, CPid2, child1, {shutdown, some_info}), + + [] = supervisor:which_children(sup_test), + [0,0,0,0] = get_child_counts(sup_test). + +%%------------------------------------------------------------------------- permanent_abnormal(doc) -> ["A permanent child should always be restarted"]; permanent_abnormal(suite) -> []; @@ -592,6 +701,37 @@ temporary_abnormal(Config) when is_list(Config) -> [0,0,0,0] = get_child_counts(sup_test). %%------------------------------------------------------------------------- +temporary_bystander(doc) -> + ["A temporary process killed as part of a rest_for_one or one_for_all " + "restart strategy should not be restarted given its args are not " + " saved. Otherwise the supervisor hits its limit and crashes."]; +temporary_bystander(suite) -> []; +temporary_bystander(_Config) -> + Child1 = {child1, {supervisor_1, start_child, []}, permanent, 100, + worker, []}, + Child2 = {child2, {supervisor_1, start_child, []}, temporary, 100, + worker, []}, + {ok, SupPid1} = supervisor:start_link(?MODULE, {ok, {{one_for_all, 2, 300}, []}}), + {ok, SupPid2} = supervisor:start_link(?MODULE, {ok, {{rest_for_one, 2, 300}, []}}), + unlink(SupPid1), % otherwise we crash with it + unlink(SupPid2), % otherwise we crash with it + {ok, CPid1} = supervisor:start_child(SupPid1, Child1), + {ok, _CPid2} = supervisor:start_child(SupPid1, Child2), + {ok, CPid3} = supervisor:start_child(SupPid2, Child1), + {ok, _CPid4} = supervisor:start_child(SupPid2, Child2), + terminate(SupPid1, CPid1, child1, normal), + terminate(SupPid2, CPid3, child1, normal), + timer:sleep(350), + catch link(SupPid1), + catch link(SupPid2), + %% The supervisor would die attempting to restart child2 + true = erlang:is_process_alive(SupPid1), + true = erlang:is_process_alive(SupPid2), + %% Child2 has not been restarted + [{child1, _, _, _}] = supervisor:which_children(SupPid1), + [{child1, _, _, _}] = supervisor:which_children(SupPid2). + +%%------------------------------------------------------------------------- one_for_one(doc) -> ["Test the one_for_one base case."]; one_for_one(suite) -> []; @@ -736,6 +876,38 @@ simple_one_for_one(Config) when is_list(Config) -> terminate(SupPid, Pid4, Id4, abnormal), check_exit([SupPid]). + +%%------------------------------------------------------------------------- +simple_one_for_one_shutdown(doc) -> + ["Test simple_one_for_one children shutdown accordingly to the " + "supervisor's shutdown strategy."]; +simple_one_for_one_shutdown(suite) -> []; +simple_one_for_one_shutdown(Config) when is_list(Config) -> + process_flag(trap_exit, true), + ShutdownTime = 1000, + Child = {child, {supervisor_2, start_child, []}, + permanent, 2*ShutdownTime, worker, []}, + {ok, SupPid} = start_link({ok, {{simple_one_for_one, 2, 3600}, [Child]}}), + + %% Will be gracefully shutdown + {ok, _CPid1} = supervisor:start_child(sup_test, [ShutdownTime]), + {ok, _CPid2} = supervisor:start_child(sup_test, [ShutdownTime]), + + %% Will be killed after 2*ShutdownTime milliseconds + {ok, _CPid3} = supervisor:start_child(sup_test, [5*ShutdownTime]), + + {T, ok} = timer:tc(fun terminate/2, [SupPid, shutdown]), + if T < 1000*ShutdownTime -> + %% Because supervisor's children wait before exiting, it can't + %% terminate quickly + test_server:fail({shutdown_too_short, T}); + T >= 1000*5*ShutdownTime -> + test_server:fail({shutdown_too_long, T}); + true -> + check_exit([SupPid]) + end. + + %%------------------------------------------------------------------------- simple_one_for_one_extra(doc) -> ["Tests automatic restart of children " @@ -972,25 +1144,25 @@ count_children_memory(Config) when is_list(Config) -> [supervisor:start_child(sup_test, []) || _Ignore <- lists:seq(1,1000)], garbage_collect(), - _Size1 = erlang:memory(processes_used), + _Size1 = proc_memory(), Children = supervisor:which_children(sup_test), - _Size2 = erlang:memory(processes_used), + _Size2 = proc_memory(), ChildCount = get_child_counts(sup_test), - _Size3 = erlang:memory(processes_used), + _Size3 = proc_memory(), [supervisor:start_child(sup_test, []) || _Ignore2 <- lists:seq(1,1000)], garbage_collect(), Children2 = supervisor:which_children(sup_test), - Size4 = erlang:memory(processes_used), + Size4 = proc_memory(), ChildCount2 = get_child_counts(sup_test), - Size5 = erlang:memory(processes_used), + Size5 = proc_memory(), garbage_collect(), Children3 = supervisor:which_children(sup_test), - Size6 = erlang:memory(processes_used), + Size6 = proc_memory(), ChildCount3 = get_child_counts(sup_test), - Size7 = erlang:memory(processes_used), + Size7 = proc_memory(), 1000 = length(Children), [1,1000,0,1000] = ChildCount, @@ -1016,16 +1188,9 @@ count_children_memory(Config) when is_list(Config) -> [terminate(SupPid, Pid, child, kill) || {undefined, Pid, worker, _Modules} <- Children3], [1,0,0,0] = get_child_counts(sup_test). -count_children_allocator_test(MemoryState) -> - Allocators = [temp_alloc, eheap_alloc, binary_alloc, ets_alloc, - driver_alloc, sl_alloc, ll_alloc, fix_alloc, std_alloc, - sys_alloc], - MemoryStateList = element(4, MemoryState), - AllocTypes = [lists:keyfind(Alloc, 1, MemoryStateList) - || Alloc <- Allocators], - AllocStates = [lists:keyfind(e, 1, AllocValue) - || {_Type, AllocValue} <- AllocTypes], - lists:all(fun(State) -> State == {e, true} end, AllocStates). +proc_memory() -> + erts_debug:set_internal_state(wait, deallocations), + erlang:memory(processes_used). %%------------------------------------------------------------------------- do_not_save_start_parameters_for_temporary_children(doc) -> @@ -1187,6 +1352,133 @@ restarted(Sup, {Id,_,_,_,_,_} = ChildSpec, TerminateHow) -> {error, {already_started, _}} = supervisor:start_child(Sup, ChildSpec). +%%------------------------------------------------------------------------- +%% OTP-9242: Pids for dynamic temporary children were saved as a list, +%% which caused bad scaling when adding/deleting many processes. +simple_one_for_one_scale_many_temporary_children(_Config) -> + process_flag(trap_exit, true), + Child = {child, {supervisor_1, start_child, []}, temporary, 1000, + worker, []}, + {ok, _SupPid} = start_link({ok, {{simple_one_for_one, 2, 3600}, [Child]}}), + + C1 = [begin + {ok,P} = supervisor:start_child(sup_test,[]), + P + end || _<- lists:seq(1,1000)], + {T1,done} = timer:tc(?MODULE,terminate_all_children,[C1]), + + C2 = [begin + {ok,P} = supervisor:start_child(sup_test,[]), + P + end || _<- lists:seq(1,10000)], + {T2,done} = timer:tc(?MODULE,terminate_all_children,[C2]), + + Scaling = T2 div T1, + if Scaling > 20 -> + %% The scaling shoul be linear (i.e.10, really), but we + %% give some extra here to avoid failing the test + %% unecessarily. + ?t:fail({bad_scaling,Scaling}); + true -> + ok + end. + + +terminate_all_children([C|Cs]) -> + ok = supervisor:terminate_child(sup_test,C), + terminate_all_children(Cs); +terminate_all_children([]) -> + done. + + +%%------------------------------------------------------------------------- +%% OTP-9212. Restart of global supervisor. +simple_global_supervisor(_Config) -> + kill_supervisor(), + kill_worker(), + exit_worker(), + restart_worker(), + ok. + +kill_supervisor() -> + {Top, Sup2_1, Server_1} = start9212(), + + %% Killing a supervisor isn't really supported, but try it anyway... + exit(Sup2_1, kill), + timer:sleep(200), + Sup2_2 = global:whereis_name(sup2), + Server_2 = global:whereis_name(server), + true = is_pid(Sup2_2), + true = is_pid(Server_2), + true = Sup2_1 =/= Sup2_2, + true = Server_1 =/= Server_2, + + stop9212(Top). + +handle_info({fail, With, After}, _State) -> + timer:sleep(After), + erlang:error(With). + +kill_worker() -> + {Top, _Sup2, Server_1} = start9212(), + exit(Server_1, kill), + timer:sleep(200), + Server_2 = global:whereis_name(server), + true = is_pid(Server_2), + true = Server_1 =/= Server_2, + stop9212(Top). + +exit_worker() -> + %% Very much the same as kill_worker(). + {Top, _Sup2, Server_1} = start9212(), + Server_1 ! {fail, normal, 0}, + timer:sleep(200), + Server_2 = global:whereis_name(server), + true = is_pid(Server_2), + true = Server_1 =/= Server_2, + stop9212(Top). + +restart_worker() -> + {Top, _Sup2, Server_1} = start9212(), + ok = supervisor:terminate_child({global, sup2}, child), + {ok, _Child} = supervisor:restart_child({global, sup2}, child), + Server_2 = global:whereis_name(server), + true = is_pid(Server_2), + true = Server_1 =/= Server_2, + stop9212(Top). + +start9212() -> + Middle = {middle,{?MODULE,middle9212,[]}, permanent,2000,supervisor,[]}, + InitResult = {ok, {{one_for_all,3,60}, [Middle]}}, + {ok, TopPid} = start_link(InitResult), + + Sup2 = global:whereis_name(sup2), + Server = global:whereis_name(server), + true = is_pid(Sup2), + true = is_pid(Server), + {TopPid, Sup2, Server}. + +stop9212(Top) -> + Old = process_flag(trap_exit, true), + exit(Top, kill), + timer:sleep(200), + undefined = global:whereis_name(sup2), + undefined = global:whereis_name(server), + check_exit([Top]), + _ = process_flag(trap_exit, Old), + ok. + +middle9212() -> + Child = {child, {?MODULE,gen_server9212,[]},permanent, 2000, worker, []}, + InitResult = {ok, {{one_for_all,3,60}, [Child]}}, + supervisor:start_link({global,sup2}, ?MODULE, InitResult). + +gen_server9212() -> + InitResult = {ok, []}, + gen_server:start_link({global,server}, ?MODULE, InitResult, []). + + +%%------------------------------------------------------------------------- terminate(Pid, Reason) when Reason =/= supervisor -> terminate(dummy, Pid, dummy, Reason). @@ -1206,6 +1498,13 @@ terminate(_, ChildPid, _, shutdown) -> {'DOWN', Ref, process, ChildPid, shutdown} -> ok end; +terminate(_, ChildPid, _, {shutdown, Term}) -> + Ref = erlang:monitor(process, ChildPid), + exit(ChildPid, {shutdown, Term}), + receive + {'DOWN', Ref, process, ChildPid, {shutdown, Term}} -> + ok + end; terminate(_, ChildPid, _, normal) -> Ref = erlang:monitor(process, ChildPid), ChildPid ! stop, diff --git a/lib/stdlib/test/supervisor_bridge_SUITE.erl b/lib/stdlib/test/supervisor_bridge_SUITE.erl index f2dbad0b3b..b3056ff41a 100644 --- a/lib/stdlib/test/supervisor_bridge_SUITE.erl +++ b/lib/stdlib/test/supervisor_bridge_SUITE.erl @@ -19,8 +19,9 @@ -module(supervisor_bridge_SUITE). -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2,starting/1, - mini_terminate/1,mini_die/1,badstart/1]). --export([client/1,init/1,internal_loop_init/1,terminate/2]). + mini_terminate/1,mini_die/1,badstart/1, + simple_global_supervisor/1]). +-export([client/1,init/1,internal_loop_init/1,terminate/2,server9212/0]). -include_lib("test_server/include/test_server.hrl"). -define(bridge_name,supervisor_bridge_SUITE_server). @@ -31,7 +32,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - [starting, mini_terminate, mini_die, badstart]. + [starting, mini_terminate, mini_die, badstart, simple_global_supervisor]. groups() -> []. @@ -138,7 +139,9 @@ init(3) -> receive {InternalPid,init_done} -> {ok,InternalPid,self()} - end. + end; +init({4,Result}) -> + Result. internal_loop_init(Parent) -> register(?work_bridge_name, self()), @@ -158,9 +161,11 @@ internal_loop(State) -> terminate(Reason,{Parent,Worker}) -> %% This func knows about supervisor_bridge io:format("Terminating bridge...\n"), - exit(kill,Worker), + exit(Worker,kill), Parent ! {dying,Reason}, - anything. + anything; +terminate(_Reason, _State) -> + any. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -197,3 +202,30 @@ badstart(Config) when is_list(Config) -> ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% OTP-9212. Restart of global supervisor. + +simple_global_supervisor(suite) -> []; +simple_global_supervisor(doc) -> "Globally registered supervisor."; +simple_global_supervisor(Config) when is_list(Config) -> + ?line Dog = test_server:timetrap({seconds,10}), + + Child = {child, {?MODULE,server9212,[]}, permanent, 2000, worker, []}, + InitResult = {ok, {{one_for_all,3,60}, [Child]}}, + {ok, Sup} = + supervisor:start_link({local,bridge9212}, ?MODULE, {4,InitResult}), + + BN_1 = global:whereis_name(?bridge_name), + ?line exit(BN_1, kill), + timer:sleep(200), + BN_2 = global:whereis_name(?bridge_name), + ?line true = is_pid(BN_2), + ?line true = BN_1 =/= BN_2, + + ?line process_flag(trap_exit, true), + exit(Sup, kill), + ?line receive {'EXIT', Sup, killed} -> ok end, + ?line test_server:timetrap_cancel(Dog), + ok. + +server9212() -> + supervisor_bridge:start_link({global,?bridge_name}, ?MODULE, 3). diff --git a/lib/stdlib/test/sys_SUITE.erl b/lib/stdlib/test/sys_SUITE.erl index 72b089aa3f..fe039e8bcc 100644 --- a/lib/stdlib/test/sys_SUITE.erl +++ b/lib/stdlib/test/sys_SUITE.erl @@ -71,7 +71,7 @@ log_to_file(Config) when is_list(Config) -> ?line ok = sys:log_to_file(?server,TempName), ?line {ok,-44} = public_call(44), ?line ok = sys:log_to_file(?server,false), - ?line {ok,Fd} = file:open(TempName,read), + ?line {ok,Fd} = file:open(TempName,[read]), ?line Msg1 = io:get_line(Fd,''), ?line Msg2 = io:get_line(Fd,''), ?line file:close(Fd), diff --git a/lib/stdlib/test/tar_SUITE.erl b/lib/stdlib/test/tar_SUITE.erl index e32704ca65..9ad3936928 100644 --- a/lib/stdlib/test/tar_SUITE.erl +++ b/lib/stdlib/test/tar_SUITE.erl @@ -65,7 +65,7 @@ borderline(Config) when is_list(Config) -> ?line {ok, Cwd} = file:get_cwd(), ?line RootDir = ?config(priv_dir, Config), - ?line TempDir = remove_prefix(Cwd++"/", filename:join(RootDir, borderline)), + ?line TempDir = remove_prefix(Cwd++"/", filename:join(RootDir, "borderline")), ?line ok = file:make_dir(TempDir), ?line Record = 512, @@ -283,17 +283,16 @@ long_names(doc) -> long_names(Config) when is_list(Config) -> ?line DataDir = ?config(data_dir, Config), ?line Long = filename:join(DataDir, "long_names.tar"), + run_in_short_tempdir(Config, + fun() -> do_long_names(Long) end). +do_long_names(Long) -> %% Try table/2 and extract/2. ?line case erl_tar:table(Long, [verbose]) of {ok,List} when is_list(List) -> ?line io:format("~p\n", [List]) end, - - %% To avoid getting too long paths for Windows to handle, extract into - %% the current directory (which is the test_server directory). Its path - %% is quite a bit shorter than the path to priv_dir. ?line {ok,Cwd} = file:get_cwd(), ?line ok = erl_tar:extract(Long), ?line Base = filename:join([Cwd, "original_software", "written_by", @@ -312,29 +311,28 @@ long_names(Config) when is_list(Config) -> ?line "Here"++_ = binary_to_list(First), ?line "And"++_ = binary_to_list(Second), - %% Clean up. - ?line delete_files([filename:join(Cwd, "original_software"),EmptyDir]), - ok. create_long_names(doc) -> ["Creates a tar file from a deep directory structure (filenames are ", "longer than 100 characters)."]; create_long_names(Config) when is_list(Config) -> - ?line PrivDir = ?config(priv_dir, Config), - ?line ok = file:set_cwd(PrivDir), - Dirs = [aslfjkshjkhliuf, - asdhjfehnbfsky, - sahajfskdfhsz, - asldfkdlfy4y8rchg, - f7nafhjgffagkhsfkhsjk, - dfjasldkfjsdkfjashbv], + run_in_short_tempdir(Config, fun create_long_names/0). + +create_long_names() -> + ?line {ok,Dir} = file:get_cwd(), + Dirs = ["aslfjkshjkhliuf", + "asdhjfehnbfsky", + "sahajfskdfhsz", + "asldfkdlfy4y8rchg", + "f7nafhjgffagkhsfkhsjk", + "dfjasldkfjsdkfjashbv"], ?line DeepDir = make_dirs(Dirs, []), ?line AFile = filename:join(DeepDir, "a_file"), ?line Hello = "hello, world\n", ?line ok = file:write_file(AFile, Hello), - ?line TarName = filename:join(PrivDir, "my_tar_with_long_names.tar"), + ?line TarName = filename:join(Dir, "my_tar_with_long_names.tar"), ?line ok = erl_tar:create(TarName, [AFile]), %% Print contents. @@ -347,9 +345,6 @@ create_long_names(Config) when is_list(Config) -> ?line {ok, Bin} = file:read_file(filename:join(ExtractDir, AFile)), ?line Hello = binary_to_list(Bin), - %% Clean up. - ?line delete_files([ExtractDir,TarName,hd(Dirs)]), - ok. make_dirs([Dir|Rest], []) -> @@ -487,7 +482,7 @@ extract_from_binary_compressed(Config) when is_list(Config) -> %% Trying extracting from a binary. ?line ok = erl_tar:extract({binary,Bin}, [compressed,{cwd,ExtractDir}]), - ?line {ok,List} = file:list_dir(filename:join(ExtractDir, ddll_SUITE_data)), + ?line {ok,List} = file:list_dir(filename:join(ExtractDir, "ddll_SUITE_data")), ?line io:format("~p\n", [List]), ?line 19 = length(List), @@ -676,7 +671,7 @@ cooked_compressed(Config) when is_list(Config) -> end, List), %% Clean up. - ?line delete_files([filename:join(PrivDir, ddll_SUITE_data)]), + ?line delete_files([filename:join(PrivDir, "ddll_SUITE_data")]), ok. memory(doc) -> @@ -734,3 +729,42 @@ delete_files([Item|Rest]) -> end, delete_files(Rest). +%% Move to a temporary directory with as short name as possible and +%% execute Fun. Remove the directory and any files in it afterwards. +%% This is necessary because pathnames on Windows may be limited to +%% 260 characters. +run_in_short_tempdir(Config, Fun) -> + {ok,Cwd} = file:get_cwd(), + PrivDir0 = ?config(priv_dir, Config), + + %% Normalize name to make sure that there is no slash at the end. + PrivDir = filename:absname(PrivDir0), + + %% We need a base directory with a much shorter pathname than + %% priv_dir. We KNOW that priv_dir is located four levels below + %% the directory that common_test puts the ct_run.* directories + %% in. That fact is not documented, but an usually reliable source + %% assured me that the directory structure is unlikely to change + %% in future versions of common_test because of backward + %% compatibility (tools developed by users of common_test depend + %% on the current directory layout). + Base = lists:foldl(fun(_, D) -> + filename:dirname(D) + end, PrivDir, [1,2,3,4]), + + Dir = make_temp_dir(Base, 0), + ok = file:set_cwd(Dir), + io:format("Running test in ~s\n", [Dir]), + try + Fun() + after + file:set_cwd(Cwd), + delete_files([Dir]) + end. + +make_temp_dir(Base, I) -> + Name = filename:join(Base, integer_to_list(I, 36)), + case file:make_dir(Name) of + ok -> Name; + {error,eexist} -> make_temp_dir(Base, I+1) + end. diff --git a/lib/stdlib/test/timer_simple_SUITE.erl b/lib/stdlib/test/timer_simple_SUITE.erl index 852afa1a4d..dc751aad16 100644 --- a/lib/stdlib/test/timer_simple_SUITE.erl +++ b/lib/stdlib/test/timer_simple_SUITE.erl @@ -229,7 +229,7 @@ cancel2(Config) when is_list(Config) -> tc(doc) -> "Test sleep/1 and tc/3."; tc(suite) -> []; tc(Config) when is_list(Config) -> - % This should both sleep and tc/3 + %% This should test both sleep and tc/3 ?line {Res1, ok} = timer:tc(timer, sleep, [500]), ?line ok = if Res1 < 500*1000 -> {too_early, Res1}; % Too early @@ -237,13 +237,40 @@ tc(Config) when is_list(Config) -> true -> ok end, - % This should both sleep and tc/2 + %% tc/2 ?line {Res2, ok} = timer:tc(fun(T) -> timer:sleep(T) end, [500]), ?line ok = if Res2 < 500*1000 -> {too_early, Res2}; % Too early Res2 > 800*1000 -> {too_late, Res2}; % Too much time true -> ok end, + + %% tc/1 + ?line {Res3, ok} = timer:tc(fun() -> timer:sleep(500) end), + ?line ok = if + Res3 < 500*1000 -> {too_early, Res3}; % Too early + Res3 > 800*1000 -> {too_late, Res3}; % Too much time + true -> ok + end, + + %% Check that timer:tc don't catch errors + ?line ok = try timer:tc(erlang, exit, [foo]) + catch exit:foo -> ok + end, + + ?line ok = try timer:tc(fun(Reason) -> 1 = Reason end, [foo]) + catch error:{badmatch,_} -> ok + end, + + ?line ok = try timer:tc(fun() -> throw(foo) end) + catch foo -> ok + end, + + %% Check that return values are propageted + Self = self(), + ?line {_, Self} = timer:tc(erlang, self, []), + ?line {_, Self} = timer:tc(fun(P) -> P end, [self()]), + ?line {_, Self} = timer:tc(fun() -> self() end), ?line Sec = timer:seconds(4), ?line Min = timer:minutes(4), diff --git a/lib/stdlib/test/unicode_SUITE.erl b/lib/stdlib/test/unicode_SUITE.erl index 9aa800209d..4055af2741 100644 --- a/lib/stdlib/test/unicode_SUITE.erl +++ b/lib/stdlib/test/unicode_SUITE.erl @@ -322,7 +322,7 @@ roundtrips(Config) when is_list(Config) -> ex_roundtrips(Config) when is_list(Config) -> ?line L1 = ranges(0, 16#D800 - 1, erlang:system_info(context_reductions) * 11), - ?line L2 = ranges(16#DFFF + 1, 16#FFFE - 1, + ?line L2 = ranges(16#DFFF + 1, 16#10000 - 1, erlang:system_info(context_reductions) * 11), %?line L3 = ranges(16#FFFF + 1, 16#10FFFF, % erlang:system_info(context_reductions) * 11), @@ -569,7 +569,6 @@ utf16_illegal_sequences_bif(Config) when is_list(Config) -> ex_utf16_illegal_sequences_bif(Config) when is_list(Config) -> ?line utf16_fail_range_bif_simple(16#10FFFF+1, 16#10FFFF+512), %Too large. ?line utf16_fail_range_bif(16#D800, 16#DFFF), %Reserved for UTF-16. - ?line utf16_fail_range_bif(16#FFFE, 16#FFFF), %Non-characters. ?line lonely_hi_surrogate_bif(16#D800, 16#DBFF,incomplete), ?line lonely_hi_surrogate_bif(16#DC00, 16#DFFF,error), @@ -644,7 +643,6 @@ utf8_illegal_sequences_bif(Config) when is_list(Config) -> ex_utf8_illegal_sequences_bif(Config) when is_list(Config) -> ?line fail_range_bif(16#10FFFF+1, 16#10FFFF+512), %Too large. ?line fail_range_bif(16#D800, 16#DFFF), %Reserved for UTF-16. - ?line fail_range_bif(16#FFFE, 16#FFFF), %Reserved (BOM). %% Illegal first character. ?line [fail_bif(<<I,16#8F,16#8F,16#8F>>,unicode) || I <- lists:seq(16#80, 16#BF)], diff --git a/lib/stdlib/test/zip_SUITE.erl b/lib/stdlib/test/zip_SUITE.erl index d5f2cd52d4..7233c061ef 100644 --- a/lib/stdlib/test/zip_SUITE.erl +++ b/lib/stdlib/test/zip_SUITE.erl @@ -375,7 +375,8 @@ zip_options(Config) when is_list(Config) -> ok = file:set_cwd(?config(data_dir, Config)), %% Create a zip archive - {ok, Zip} = zip:zip("filename_not_used.zip", Names, [memory, {cwd, PrivDir}]), + {ok, {_,Zip}} = + zip:zip("filename_not_used.zip", Names, [memory, {cwd, PrivDir}]), %% Open archive {ok, ZipSrv} = zip:zip_open(Zip, [memory]), |