diff options
Diffstat (limited to 'erts/emulator/test/bif_SUITE.erl')
-rw-r--r-- | erts/emulator/test/bif_SUITE.erl | 677 |
1 files changed, 461 insertions, 216 deletions
diff --git a/erts/emulator/test/bif_SUITE.erl b/erts/emulator/test/bif_SUITE.erl index fbc229bc53..339c827602 100644 --- a/erts/emulator/test/bif_SUITE.erl +++ b/erts/emulator/test/bif_SUITE.erl @@ -1,76 +1,54 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2012. All Rights Reserved. +%% Copyright Ericsson AB 2005-2016. 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/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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. +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% -module(bif_SUITE). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). +-include_lib("kernel/include/file.hrl"). --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, - init_per_testcase/2,end_per_testcase/2, +-export([all/0, suite/0, display/1, display_huge/0, erl_bif_types/1,guard_bifs_in_erl_bif_types/1, - shadow_comments/1, + shadow_comments/1,list_to_utf8_atom/1, specs/1,improper_bif_stubs/1,auto_imports/1, t_list_to_existing_atom/1,os_env/1,otp_7526/1, binary_to_atom/1,binary_to_existing_atom/1, - atom_to_binary/1,min_max/1, erlang_halt/1]). + atom_to_binary/1,min_max/1, erlang_halt/1, + erl_crash_dump_bytes/1, + is_builtin/1, error_stacktrace/1, + error_stacktrace_during_call_trace/1]). -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {minutes, 1}}]. all() -> [erl_bif_types, guard_bifs_in_erl_bif_types, shadow_comments, specs, improper_bif_stubs, auto_imports, t_list_to_existing_atom, os_env, otp_7526, - display, + display, list_to_utf8_atom, atom_to_binary, binary_to_atom, binary_to_existing_atom, - min_max, erlang_halt]. - -groups() -> - []. - -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - -init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) -> - Dog=?t:timetrap(?t:minutes(1)), - [{watchdog, Dog}|Config]. + erl_crash_dump_bytes, min_max, erlang_halt, is_builtin, + error_stacktrace, error_stacktrace_during_call_trace]. -end_per_testcase(_Func, Config) -> - Dog=?config(watchdog, Config), - ?t:timetrap_cancel(Dog). - - -display(suite) -> - []; -display(doc) -> - ["Uses erlang:display to test that erts_printf does not do deep recursion"]; +%% Uses erlang:display to test that erts_printf does not do deep recursion display(Config) when is_list(Config) -> Pa = filename:dirname(code:which(?MODULE)), {ok, Node} = test_server:start_node(display_huge_term,peer, @@ -115,7 +93,7 @@ erl_bif_types_2(List) -> [_|_] -> io:put_chars("Bifs with bad arity\n"), io:format("~p\n", [BadArity]), - ?line ?t:fail({length(BadArity),bad_arity}) + ct:fail({length(BadArity),bad_arity}) end. erl_bif_types_3(List) -> @@ -139,7 +117,7 @@ erl_bif_types_3(List) -> io:put_chars("Bifs with failing calls to erlang_bif_types:type/3 " "(or with bogus return values):\n"), io:format("~p\n", [BadSmokeTest]), - ?line ?t:fail({length(BadSmokeTest),bad_smoke_test}) + ct:fail({length(BadSmokeTest),bad_smoke_test}) end. guard_bifs_in_erl_bif_types(_Config) -> @@ -160,15 +138,17 @@ guard_bifs_in_erl_bif_types(_Config) -> "The following guard BIFs have no type information " "in erl_bif_types:\n\n", [io_lib:format(" ~p/~p\n", [F,A]) || {F,A} <- Not]]), - ?t:fail() + ct:fail(erl_bif_types) end. shadow_comments(_Config) -> ensure_erl_bif_types_compiled(), + ErlangList = [{erlang,F,A} || {F,A} <- erlang:module_info(exports), + not is_operator(F,A)], List0 = erlang:system_info(snifs), - List1 = [MFA || {M,_,_}=MFA <- List0, M =/= hipe_bifs], - List = [MFA || MFA <- List1, not is_operator(MFA)], + List1 = [MFA || {M,_,_}=MFA <- List0, M =/= hipe_bifs, M =/= erlang], + List = List1 ++ ErlangList, HasTypes = [MFA || {M,F,A}=MFA <- List, erl_bif_types:is_known(M, F, A)], Path = get_code_path(), @@ -200,7 +180,7 @@ shadow_comments(_Config) -> "obvious.\n\nThe following comments are missing:\n\n", [io_lib:format("%% Shadowed by erl_bif_types: ~p:~p/~p\n", [M,F,A]) || {M,F,A} <- NoComments]]), - ?t:fail() + ct:fail(bif_stub) end, case NoBifSpecs of @@ -214,7 +194,7 @@ shadow_comments(_Config) -> "Therefore, the following comments should be removed:\n\n", [io_lib:format("%% Shadowed by erl_bif_types: ~p:~p/~p\n", [M,F,A]) || {M,F,A} <- NoBifSpecs]]), - ?t:fail() + ct:fail(erl_bif_types) end. extract_comments(Mod, Path) -> @@ -236,7 +216,7 @@ ensure_erl_bif_types_compiled() -> case erlang:function_exported(erl_bif_types, module_info, 0) of false -> %% Fail cleanly. - ?t:fail("erl_bif_types not compiled"); + ct:fail("erl_bif_types not compiled"); true -> ok end. @@ -274,16 +254,19 @@ specs(_) -> [_|_] -> io:put_chars("The following BIFs don't have specs:\n"), [print_mfa(MFA) || MFA <- NoSpecs], - ?t:fail() + ct:fail(no_spec) end. is_operator({erlang,F,A}) -> + is_operator(F,A); +is_operator(_) -> false. + +is_operator(F,A) -> erl_internal:arith_op(F, A) orelse erl_internal:bool_op(F, A) orelse erl_internal:comp_op(F, A) orelse erl_internal:list_op(F, A) orelse - erl_internal:send_op(F, A); -is_operator(_) -> false. + erl_internal:send_op(F, A). extract_specs(M, Abstr) -> [{make_mfa(M, Name),Spec} || {attribute,_,spec,{Name,Spec}} <- Abstr]. @@ -334,7 +317,7 @@ auto_imports([{erlang,F,A}|T], Errors) -> auto_imports([], 0) -> ok; auto_imports([], Errors) -> - ?t:fail({Errors,inconsistencies}). + ct:fail({Errors,inconsistencies}). extract_functions(M, Abstr) -> [{{M,F,A},Body} || {function,_,F,A,Body} <- Abstr]. @@ -353,40 +336,68 @@ check_stub({_,F,A}, B) -> io:put_chars(erl_pp:function(Func)), io:nl(), io:put_chars("The body should be: erlang:nif_error(undef)"), - ?t:fail() + ct:fail(invalid_body) end. +list_to_utf8_atom(Config) when is_list(Config) -> + 'hello' = atom_roundtrip("hello"), + 'こんにちは' = atom_roundtrip("こんにちは"), + + %% Test all edge cases. + _ = atom_roundtrip([16#80]), + _ = atom_roundtrip([16#7F]), + _ = atom_roundtrip([16#FF]), + _ = atom_roundtrip([16#100]), + _ = atom_roundtrip([16#7FF]), + _ = atom_roundtrip([16#800]), + _ = atom_roundtrip([16#D7FF]), + atom_badarg([16#D800]), + atom_badarg([16#DFFF]), + _ = atom_roundtrip([16#E000]), + _ = atom_roundtrip([16#FFFF]), + _ = atom_roundtrip([16#1000]), + _ = atom_roundtrip([16#10FFFF]), + atom_badarg([16#110000]), + ok. + +atom_roundtrip(String) -> + Atom = list_to_atom(String), + Atom = list_to_existing_atom(String), + String = atom_to_list(Atom), + Atom. + +atom_badarg(String) -> + {'EXIT',{badarg,_}} = (catch list_to_atom(String)), + {'EXIT',{badarg,_}} = (catch list_to_existing_atom(String)), + ok. + t_list_to_existing_atom(Config) when is_list(Config) -> - ?line all = list_to_existing_atom("all"), - ?line ?MODULE = list_to_existing_atom(?MODULE_STRING), - ?line UnlikelyStr = "dsfj923874390867er869fds9864y97jhg3973qerueoru", + all = list_to_existing_atom("all"), + ?MODULE = list_to_existing_atom(?MODULE_STRING), + UnlikelyStr = "dsfj923874390867er869fds9864y97jhg3973qerueoru", try - ?line list_to_existing_atom(UnlikelyStr), - ?line ?t:fail() + list_to_existing_atom(UnlikelyStr), + ct:fail(atom_exists) catch error:badarg -> ok end, %% The compiler has become smarter! We need the call to id/1 in %% the next line. - ?line UnlikelyAtom = list_to_atom(id(UnlikelyStr)), - ?line UnlikelyAtom = list_to_existing_atom(UnlikelyStr), + UnlikelyAtom = list_to_atom(id(UnlikelyStr)), + UnlikelyAtom = list_to_existing_atom(UnlikelyStr), ok. -os_env(doc) -> - []; -os_env(suite) -> - []; os_env(Config) when is_list(Config) -> - ?line EnvVar1 = "MjhgvFDrresdCghN mnjkUYg vfrD", - ?line false = os:getenv(EnvVar1), - ?line true = os:putenv(EnvVar1, "mors"), - ?line "mors" = os:getenv(EnvVar1), - ?line true = os:putenv(EnvVar1, ""), - ?line case os:getenv(EnvVar1) of - "" -> ?line ok; - false -> ?line ok; - BadVal -> ?line ?t:fail(BadVal) + EnvVar1 = "MjhgvFDrresdCghN mnjkUYg vfrD", + false = os:getenv(EnvVar1), + true = os:putenv(EnvVar1, "mors"), + "mors" = os:getenv(EnvVar1), + true = os:putenv(EnvVar1, ""), + case os:getenv(EnvVar1) of + "" -> ok; + false -> ok; + BadVal -> ct:fail(BadVal) end, true = os:putenv(EnvVar1, "mors"), true = os:unsetenv(EnvVar1), @@ -394,19 +405,18 @@ os_env(Config) when is_list(Config) -> true = os:unsetenv(EnvVar1), % unset unset variable %% os:putenv, os:getenv and os:unsetenv currently use a temp %% buffer of size 1024 for storing key+value - ?line os_env_long(1010, 1030, "hej hopp"). + os_env_long(1010, 1030, "hej hopp"). os_env_long(Min, Max, _Value) when Min > Max -> - ?line ok; + ok; os_env_long(Min, Max, Value) -> - ?line EnvVar = lists:duplicate(Min, $X), - ?line true = os:putenv(EnvVar, Value), - ?line Value = os:getenv(EnvVar), + EnvVar = lists:duplicate(Min, $X), + true = os:putenv(EnvVar, Value), + Value = os:getenv(EnvVar), true = os:unsetenv(EnvVar), - ?line os_env_long(Min+1, Max, Value). + os_env_long(Min+1, Max, Value). -otp_7526(doc) -> - ["Test that string:to_integer does not Halloc in wrong order."]; +%% Test that string:to_integer does not Halloc in wrong order. otp_7526(Config) when is_list(Config) -> ok = test_7526(256). @@ -421,15 +431,15 @@ do_test_7526(N,M) -> {Self, Ref} = {self(), make_ref()}, T = erlang:make_tuple(M,0), spawn_opt(fun()-> - L = iterate_7526(N, []), - BadList = [X || X <- L, X =/= 9223372036854775808], - BadLen = length(BadList), - M = length(tuple_to_list(T)), - %%io:format("~b bad conversions: ~p~n", [BadLen, BadList]), - Self ! {done, Ref, BadLen} - end, - [link,{fullsweep_after,0}]), - receive {done, Ref, Len} -> Len end. + L = iterate_7526(N, []), + BadList = [X || X <- L, X =/= 9223372036854775808], + BadLen = length(BadList), + M = length(tuple_to_list(T)), + %%io:format("~b bad conversions: ~p~n", [BadLen, BadList]), + Self ! {done, Ref, BadLen} + end, + [link,{fullsweep_after,0}]), + receive {done, Ref, Len} -> Len end. test_7526(0) -> @@ -451,59 +461,58 @@ binary_to_atom(Config) when is_list(Config) -> Long = lists:seq(0, 254), LongAtom = list_to_atom(Long), LongBin = list_to_binary(Long), + UnicodeLongAtom = list_to_atom([$é || _ <- lists:seq(0, 254)]), + UnicodeLongBin = << <<"é"/utf8>> || _ <- lists:seq(0, 254)>>, %% latin1 - ?line '' = test_binary_to_atom(<<>>, latin1), - ?line '\377' = test_binary_to_atom(<<255>>, latin1), - ?line HalfLongAtom = test_binary_to_atom(HalfLongBin, latin1), - ?line LongAtom = test_binary_to_atom(LongBin, latin1), + '' = test_binary_to_atom(<<>>, latin1), + '\377' = test_binary_to_atom(<<255>>, latin1), + HalfLongAtom = test_binary_to_atom(HalfLongBin, latin1), + LongAtom = test_binary_to_atom(LongBin, latin1), %% utf8 - ?line '' = test_binary_to_atom(<<>>, utf8), - ?line HalfLongAtom = test_binary_to_atom(HalfLongBin, utf8), - ?line HalfLongAtom = test_binary_to_atom(HalfLongBin, unicode), - ?line [] = [C || C <- lists:seq(128, 255), + '' = test_binary_to_atom(<<>>, utf8), + HalfLongAtom = test_binary_to_atom(HalfLongBin, utf8), + HalfLongAtom = test_binary_to_atom(HalfLongBin, unicode), + UnicodeLongAtom = test_binary_to_atom(UnicodeLongBin, utf8), + UnicodeLongAtom = test_binary_to_atom(UnicodeLongBin, unicode), + [] = [C || C <- lists:seq(128, 255), begin list_to_atom([C]) =/= test_binary_to_atom(<<C/utf8>>, utf8) end], + <<"こんにちは"/utf8>> = + atom_to_binary(test_binary_to_atom(<<"こんにちは"/utf8>>, utf8), utf8), + %% badarg failures. - ?line fail_binary_to_atom(atom), - ?line fail_binary_to_atom(42), - ?line fail_binary_to_atom({a,b,c}), - ?line fail_binary_to_atom([1,2,3]), - ?line fail_binary_to_atom([]), - ?line fail_binary_to_atom(42.0), - ?line fail_binary_to_atom(self()), - ?line fail_binary_to_atom(make_ref()), - ?line fail_binary_to_atom(<<0:7>>), - ?line fail_binary_to_atom(<<42:13>>), - ?line ?BADARG(binary_to_atom(id(<<>>), blurf)), - ?line ?BADARG(binary_to_atom(id(<<>>), [])), + fail_binary_to_atom(atom), + fail_binary_to_atom(42), + fail_binary_to_atom({a,b,c}), + fail_binary_to_atom([1,2,3]), + fail_binary_to_atom([]), + fail_binary_to_atom(42.0), + fail_binary_to_atom(self()), + fail_binary_to_atom(make_ref()), + fail_binary_to_atom(<<0:7>>), + fail_binary_to_atom(<<42:13>>), + ?BADARG(binary_to_atom(id(<<>>), blurf)), + ?BADARG(binary_to_atom(id(<<>>), [])), %% Bad UTF8 sequences. - ?line ?BADARG(binary_to_atom(id(<<255>>), utf8)), - ?line ?BADARG(binary_to_atom(id(<<255,0>>), utf8)), - ?line ?BADARG(binary_to_atom(id(<<16#C0,16#80>>), utf8)), %Overlong 0. - ?line [?BADARG(binary_to_atom(<<C/utf8>>, utf8)) || - C <- lists:seq(256, 16#D7FF)], - ?line [?BADARG(binary_to_atom(<<C/utf8>>, utf8)) || - C <- lists:seq(16#E000, 16#FFFD)], - ?line [?BADARG(binary_to_atom(<<C/utf8>>, utf8)) || - C <- lists:seq(16#10000, 16#8FFFF)], - ?line [?BADARG(binary_to_atom(<<C/utf8>>, utf8)) || - C <- lists:seq(16#90000, 16#10FFFF)], + ?BADARG(binary_to_atom(id(<<255>>), utf8)), + ?BADARG(binary_to_atom(id(<<255,0>>), utf8)), + ?BADARG(binary_to_atom(id(<<16#C0,16#80>>), utf8)), %Overlong 0. %% system_limit failures. - ?line ?SYS_LIMIT(binary_to_atom(id(<<0:512/unit:8,255>>), utf8)), - ?line ?SYS_LIMIT(binary_to_atom(id(<<0:512/unit:8,255,0>>), utf8)), - ?line ?SYS_LIMIT(binary_to_atom(<<0:256/unit:8>>, latin1)), - ?line ?SYS_LIMIT(binary_to_atom(<<0:257/unit:8>>, latin1)), - ?line ?SYS_LIMIT(binary_to_atom(<<0:512/unit:8>>, latin1)), - ?line ?SYS_LIMIT(binary_to_atom(<<0:256/unit:8>>, utf8)), - ?line ?SYS_LIMIT(binary_to_atom(<<0:257/unit:8>>, utf8)), - ?line ?SYS_LIMIT(binary_to_atom(<<0:512/unit:8>>, utf8)), + ?SYS_LIMIT(binary_to_atom(id(<<0:512/unit:8,255>>), utf8)), + ?SYS_LIMIT(binary_to_atom(id(<<0:512/unit:8,255,0>>), utf8)), + ?SYS_LIMIT(binary_to_atom(<<0:256/unit:8>>, latin1)), + ?SYS_LIMIT(binary_to_atom(<<0:257/unit:8>>, latin1)), + ?SYS_LIMIT(binary_to_atom(<<0:512/unit:8>>, latin1)), + ?SYS_LIMIT(binary_to_atom(<<0:256/unit:8>>, utf8)), + ?SYS_LIMIT(binary_to_atom(<<0:257/unit:8>>, utf8)), + ?SYS_LIMIT(binary_to_atom(<<0:512/unit:8>>, utf8)), ok. test_binary_to_atom(Bin0, Encoding) -> @@ -516,49 +525,49 @@ test_binary_to_atom(Bin0, Encoding) -> fail_binary_to_atom(Bin) -> try - binary_to_atom(Bin, latin1) + binary_to_atom(Bin, latin1) catch - error:badarg -> - ok + error:badarg -> + ok end, try - binary_to_atom(Bin, utf8) + binary_to_atom(Bin, utf8) catch - error:badarg -> - ok + error:badarg -> + ok end, try - binary_to_existing_atom(Bin, latin1) + binary_to_existing_atom(Bin, latin1) catch - error:badarg -> - ok + error:badarg -> + ok end, try - binary_to_existing_atom(Bin, utf8) + binary_to_existing_atom(Bin, utf8) catch - error:badarg -> - ok + error:badarg -> + ok end. binary_to_existing_atom(Config) when is_list(Config) -> - ?line UnlikelyBin = <<"ou0897979655678dsfj923874390867er869fds973qerueoru">>, + UnlikelyBin = <<"ou0897979655678dsfj923874390867er869fds973qerueoru">>, try - ?line binary_to_existing_atom(UnlikelyBin, latin1), - ?line ?t:fail() + binary_to_existing_atom(UnlikelyBin, latin1), + ct:fail(atom_exists) catch error:badarg -> ok end, try - ?line binary_to_existing_atom(UnlikelyBin, utf8), - ?line ?t:fail() + binary_to_existing_atom(UnlikelyBin, utf8), + ct:fail(atom_exists) catch error:badarg -> ok end, - ?line UnlikelyAtom = binary_to_atom(id(UnlikelyBin), latin1), - ?line UnlikelyAtom = binary_to_existing_atom(UnlikelyBin, latin1), + UnlikelyAtom = binary_to_atom(id(UnlikelyBin), latin1), + UnlikelyAtom = binary_to_existing_atom(UnlikelyBin, latin1), ok. @@ -571,32 +580,32 @@ atom_to_binary(Config) when is_list(Config) -> LongBin = list_to_binary(Long), %% latin1 - ?line <<>> = atom_to_binary('', latin1), - ?line <<"abc">> = atom_to_binary(abc, latin1), - ?line <<127>> = atom_to_binary('\177', latin1), - ?line HalfLongBin = atom_to_binary(HalfLongAtom, latin1), - ?line LongBin = atom_to_binary(LongAtom, latin1), + <<>> = atom_to_binary('', latin1), + <<"abc">> = atom_to_binary(abc, latin1), + <<127>> = atom_to_binary('\177', latin1), + HalfLongBin = atom_to_binary(HalfLongAtom, latin1), + LongBin = atom_to_binary(LongAtom, latin1), %% utf8. - ?line <<>> = atom_to_binary('', utf8), - ?line <<>> = atom_to_binary('', unicode), - ?line <<127>> = atom_to_binary('\177', utf8), - ?line <<"abcdef">> = atom_to_binary(abcdef, utf8), - ?line HalfLongBin = atom_to_binary(HalfLongAtom, utf8), - ?line LongAtomBin = atom_to_binary(LongAtom, utf8), - ?line verify_long_atom_bin(LongAtomBin, 0), + <<>> = atom_to_binary('', utf8), + <<>> = atom_to_binary('', unicode), + <<127>> = atom_to_binary('\177', utf8), + <<"abcdef">> = atom_to_binary(abcdef, utf8), + HalfLongBin = atom_to_binary(HalfLongAtom, utf8), + LongAtomBin = atom_to_binary(LongAtom, utf8), + verify_long_atom_bin(LongAtomBin, 0), %% Failing cases. - ?line fail_atom_to_binary(<<1>>), - ?line fail_atom_to_binary(42), - ?line fail_atom_to_binary({a,b,c}), - ?line fail_atom_to_binary([1,2,3]), - ?line fail_atom_to_binary([]), - ?line fail_atom_to_binary(42.0), - ?line fail_atom_to_binary(self()), - ?line fail_atom_to_binary(make_ref()), - ?line ?BADARG(atom_to_binary(id(a), blurf)), - ?line ?BADARG(atom_to_binary(id(b), [])), + fail_atom_to_binary(<<1>>), + fail_atom_to_binary(42), + fail_atom_to_binary({a,b,c}), + fail_atom_to_binary([1,2,3]), + fail_atom_to_binary([]), + fail_atom_to_binary(42.0), + fail_atom_to_binary(self()), + fail_atom_to_binary(make_ref()), + ?BADARG(atom_to_binary(id(a), blurf)), + ?BADARG(atom_to_binary(id(b), [])), ok. verify_long_atom_bin(<<I/utf8,T/binary>>, I) -> @@ -605,74 +614,73 @@ verify_long_atom_bin(<<>>, 255) -> ok. fail_atom_to_binary(Term) -> try - atom_to_binary(Term, latin1) + atom_to_binary(Term, latin1) catch - error:badarg -> - ok + error:badarg -> + ok end, try - atom_to_binary(Term, utf8) + atom_to_binary(Term, utf8) catch - error:badarg -> - ok + error:badarg -> + ok end. min_max(Config) when is_list(Config) -> - ?line a = erlang:min(id(a), a), - ?line a = erlang:min(id(a), b), - ?line a = erlang:min(id(b), a), - ?line b = erlang:min(id(b), b), - ?line a = erlang:max(id(a), a), - ?line b = erlang:max(id(a), b), - ?line b = erlang:max(id(b), a), - ?line b = erlang:max(id(b), b), - - ?line 42.0 = erlang:min(42.0, 42), - ?line 42.0 = erlang:max(42.0, 42), + a = erlang:min(id(a), a), + a = erlang:min(id(a), b), + a = erlang:min(id(b), a), + b = erlang:min(id(b), b), + a = erlang:max(id(a), a), + b = erlang:max(id(a), b), + b = erlang:max(id(b), a), + b = erlang:max(id(b), b), + + 42.0 = erlang:min(42.0, 42), + 42.0 = erlang:max(42.0, 42), %% And now (R14) they are also autoimported! - ?line a = min(id(a), a), - ?line a = min(id(a), b), - ?line a = min(id(b), a), - ?line b = min(id(b), b), - ?line a = max(id(a), a), - ?line b = max(id(a), b), - ?line b = max(id(b), a), - ?line b = max(id(b), b), - - ?line 42.0 = min(42.0, 42), - ?line 42.0 = max(42.0, 42), - + a = min(id(a), a), + a = min(id(a), b), + a = min(id(b), a), + b = min(id(b), b), + a = max(id(a), a), + b = max(id(a), b), + b = max(id(b), a), + b = max(id(b), b), + + 42.0 = min(42.0, 42), + 42.0 = max(42.0, 42), ok. erlang_halt(Config) when is_list(Config) -> try erlang:halt(undefined) of - _-> ?t:fail({erlang,halt,{undefined}}) + _-> ct:fail({erlang,halt,{undefined}}) catch error:badarg -> ok end, try halt(undefined) of - _-> ?t:fail({halt,{undefined}}) + _-> ct:fail({halt,{undefined}}) catch error:badarg -> ok end, try erlang:halt(undefined, []) of - _-> ?t:fail({erlang,halt,{undefined,[]}}) + _-> ct:fail({erlang,halt,{undefined,[]}}) catch error:badarg -> ok end, try halt(undefined, []) of - _-> ?t:fail({halt,{undefined,[]}}) + _-> ct:fail({halt,{undefined,[]}}) catch error:badarg -> ok end, try halt(0, undefined) of - _-> ?t:fail({halt,{0,undefined}}) + _-> ct:fail({halt,{0,undefined}}) catch error:badarg -> ok end, try halt(0, [undefined]) of - _-> ?t:fail({halt,{0,[undefined]}}) + _-> ct:fail({halt,{0,[undefined]}}) catch error:badarg -> ok end, try halt(0, [{undefined,true}]) of - _-> ?t:fail({halt,{0,[{undefined,true}]}}) + _-> ct:fail({halt,{0,[{undefined,true}]}}) catch error:badarg -> ok end, try halt(0, [{flush,undefined}]) of - _-> ?t:fail({halt,{0,[{flush,undefined}]}}) + _-> ct:fail({halt,{0,[{flush,undefined}]}}) catch error:badarg -> ok end, try halt(0, [{flush,true,undefined}]) of - _-> ?t:fail({halt,{0,[{flush,true,undefined}]}}) + _-> ct:fail({halt,{0,[{flush,true,undefined}]}}) catch error:badarg -> ok end, H = hostname(), {ok,N1} = slave:start(H, halt_node1), @@ -681,8 +689,245 @@ erlang_halt(Config) when is_list(Config) -> {badrpc,nodedown} = rpc:call(N2, erlang, halt, [0]), {ok,N3} = slave:start(H, halt_node3), {badrpc,nodedown} = rpc:call(N3, erlang, halt, [0,[]]), + {ok,N4} = slave:start(H, halt_node4), + {badrpc,nodedown} = rpc:call(N4, erlang, halt, [lists:duplicate(300,$x)]), + + % This test triggers a segfault when dumping a crash dump + % to make sure that we can handle it properly. + {ok,N4} = slave:start(H, halt_node4), + CrashDump = filename:join(proplists:get_value(priv_dir,Config), + "segfault_erl_crash.dump"), + true = rpc:call(N4, os, putenv, ["ERL_CRASH_DUMP",CrashDump]), + false = rpc:call(N4, erts_debug, set_internal_state, + [available_internal_state, true]), + {badrpc,nodedown} = rpc:call(N4, erts_debug, set_internal_state, + [broken_halt, "Validate correct crash dump"]), + {ok,_} = wait_until_stable_size(CrashDump,-1), + {ok, Bin} = file:read_file(CrashDump), + case {string:str(binary_to_list(Bin),"\n=end\n"), + string:str(binary_to_list(Bin),"\r\n=end\r\n")} of + {0,0} -> ct:fail("Could not find end marker in crash dump"); + _ -> ok + end. + +wait_until_stable_size(_File,-10) -> + {error,enoent}; +wait_until_stable_size(File,PrevSz) -> + timer:sleep(250), + case file:read_file_info(File) of + {error,enoent} -> + wait_until_stable_size(File,PrevSz-1); + {ok,#file_info{size = PrevSz }} when PrevSz /= -1 -> + io:format("Crashdump file size was: ~p (~s)~n",[PrevSz,File]), + {ok,PrevSz}; + {ok,#file_info{size = NewSz }} -> + wait_until_stable_size(File,NewSz) + end. + +% Test erlang:halt with ERL_CRASH_DUMP_BYTES +erl_crash_dump_bytes(Config) when is_list(Config) -> + Bytes = 1000, + CrashDump = do_limited_crash_dump(Config, Bytes), + {ok,ActualBytes} = wait_until_stable_size(CrashDump,-1), + true = ActualBytes < (Bytes + 100), + + NoDump = do_limited_crash_dump(Config,0), + {error,enoent} = wait_until_stable_size(NoDump,-8), ok. +do_limited_crash_dump(Config, Bytes) -> + H = hostname(), + {ok,N} = slave:start(H, halt_node), + BytesStr = integer_to_list(Bytes), + CrashDump = filename:join(proplists:get_value(priv_dir,Config), + "erl_crash." ++ BytesStr ++ ".dump"), + true = rpc:call(N, os, putenv, ["ERL_CRASH_DUMP",CrashDump]), + true = rpc:call(N, os, putenv, ["ERL_CRASH_DUMP_BYTES",BytesStr]), + {badrpc,nodedown} = rpc:call(N, erlang, halt, ["Testing ERL_CRASH_DUMP_BYTES"]), + CrashDump. + + +is_builtin(_Config) -> + Exp0 = [{M,F,A} || {M,_} <- code:all_loaded(), + {F,A} <- M:module_info(exports)], + Exp = ordsets:from_list(Exp0), + + %% erlang:apply/3 is considered to be built-in, but is not + %% implemented as other BIFs. + + Builtins0 = [{erlang,apply,3}|erlang:system_info(snifs)], + Builtins = ordsets:from_list(Builtins0), + NotBuiltin = ordsets:subtract(Exp, Builtins), + _ = [true = erlang:is_builtin(M, F, A) || {M,F,A} <- Builtins], + _ = [false = erlang:is_builtin(M, F, A) || {M,F,A} <- NotBuiltin], + + ok. + +error_stacktrace(Config) when is_list(Config) -> + error_stacktrace_test(). + +error_stacktrace_during_call_trace(Config) when is_list(Config) -> + Tracer = spawn_link(fun () -> + receive after infinity -> ok end + end), + Mprog = [{'_',[],[{exception_trace}]}], + erlang:trace_pattern({?MODULE,'_','_'}, Mprog, [local]), + 1 = erlang:trace_pattern({erlang,error,2}, Mprog, [local]), + 1 = erlang:trace_pattern({erlang,error,1}, Mprog, [local]), + erlang:trace(all, true, [call,return_to,timestamp,{tracer, Tracer}]), + try + error_stacktrace_test() + after + erlang:trace(all, false, [call,return_to,timestamp,{tracer, Tracer}]), + erlang:trace_pattern({erlang,error,2}, false, [local]), + erlang:trace_pattern({erlang,error,1}, false, [local]), + erlang:trace_pattern({?MODULE,'_','_'}, false, [local]), + unlink(Tracer), + exit(Tracer, kill), + Mon = erlang:monitor(process, Tracer), + receive + {'DOWN', Mon, process, Tracer, _} -> ok + end + end, + ok. + + +error_stacktrace_test() -> + Types = [apply_const_last, apply_const, apply_last, + apply, double_apply_const_last, double_apply_const, + double_apply_last, double_apply, multi_apply_const_last, + multi_apply_const, multi_apply_last, multi_apply, + call_const_last, call_last, call_const, call], + lists:foreach(fun (Type) -> + {Pid, Mon} = spawn_monitor( + fun () -> + stk([a,b,c,d], Type, error_2) + end), + receive + {'DOWN', Mon, process, Pid, Reason} -> + {oops, Stack} = Reason, +%% io:format("Type: ~p Stack: ~p~n", +%% [Type, Stack]), + [{?MODULE, do_error_2, [Type], _}, + {?MODULE, stk, 3, _}, + {?MODULE, stk, 3, _}] = Stack + end + end, + Types), + lists:foreach(fun (Type) -> + {Pid, Mon} = spawn_monitor( + fun () -> + stk([a,b,c,d], Type, error_1) + end), + receive + {'DOWN', Mon, process, Pid, Reason} -> + {oops, Stack} = Reason, +%% io:format("Type: ~p Stack: ~p~n", +%% [Type, Stack]), + [{?MODULE, do_error_1, 1, _}, + {?MODULE, stk, 3, _}, + {?MODULE, stk, 3, _}] = Stack + end + end, + Types), + ok. + +stk([], Type, Func) -> + tail(Type, Func, jump), + ok; +stk([_|L], Type, Func) -> + stk(L, Type, Func), + ok. + +tail(Type, Func, jump) -> + tail(Type, Func, do); +tail(Type, error_1, do) -> + do_error_1(Type); +tail(Type, error_2, do) -> + do_error_2(Type). + +do_error_2(apply_const_last) -> + erlang:apply(erlang, error, [oops, [apply_const_last]]); +do_error_2(apply_const) -> + erlang:apply(erlang, error, [oops, [apply_const]]), + ok; +do_error_2(apply_last) -> + erlang:apply(id(erlang), id(error), id([oops, [apply_last]])); +do_error_2(apply) -> + erlang:apply(id(erlang), id(error), id([oops, [apply]])), + ok; +do_error_2(double_apply_const_last) -> + erlang:apply(erlang, apply, [erlang, error, [oops, [double_apply_const_last]]]); +do_error_2(double_apply_const) -> + erlang:apply(erlang, apply, [erlang, error, [oops, [double_apply_const]]]), + ok; +do_error_2(double_apply_last) -> + erlang:apply(id(erlang), id(apply), [id(erlang), id(error), id([oops, [double_apply_last]])]); +do_error_2(double_apply) -> + erlang:apply(id(erlang), id(apply), [id(erlang), id(error), id([oops, [double_apply]])]), + ok; +do_error_2(multi_apply_const_last) -> + erlang:apply(erlang, apply, [erlang, apply, [erlang, apply, [erlang, error, [oops, [multi_apply_const_last]]]]]); +do_error_2(multi_apply_const) -> + erlang:apply(erlang, apply, [erlang, apply, [erlang, apply, [erlang, error, [oops, [multi_apply_const]]]]]), + ok; +do_error_2(multi_apply_last) -> + erlang:apply(id(erlang), id(apply), [id(erlang), id(apply), [id(erlang), id(apply), [id(erlang), id(error), id([oops, [multi_apply_last]])]]]); +do_error_2(multi_apply) -> + erlang:apply(id(erlang), id(apply), [id(erlang), id(apply), [id(erlang), id(apply), [id(erlang), id(error), id([oops, [multi_apply]])]]]), + ok; +do_error_2(call_const_last) -> + erlang:error(oops, [call_const_last]); +do_error_2(call_last) -> + erlang:error(id(oops), id([call_last])); +do_error_2(call_const) -> + erlang:error(oops, [call_const]), + ok; +do_error_2(call) -> + erlang:error(id(oops), id([call])). + + +do_error_1(apply_const_last) -> + erlang:apply(erlang, error, [oops]); +do_error_1(apply_const) -> + erlang:apply(erlang, error, [oops]), + ok; +do_error_1(apply_last) -> + erlang:apply(id(erlang), id(error), id([oops])); +do_error_1(apply) -> + erlang:apply(id(erlang), id(error), id([oops])), + ok; +do_error_1(double_apply_const_last) -> + erlang:apply(erlang, apply, [erlang, error, [oops]]); +do_error_1(double_apply_const) -> + erlang:apply(erlang, apply, [erlang, error, [oops]]), + ok; +do_error_1(double_apply_last) -> + erlang:apply(id(erlang), id(apply), [id(erlang), id(error), id([oops])]); +do_error_1(double_apply) -> + erlang:apply(id(erlang), id(apply), [id(erlang), id(error), id([oops])]), + ok; +do_error_1(multi_apply_const_last) -> + erlang:apply(erlang, apply, [erlang, apply, [erlang, apply, [erlang, error, [oops]]]]); +do_error_1(multi_apply_const) -> + erlang:apply(erlang, apply, [erlang, apply, [erlang, apply, [erlang, error, [oops]]]]), + ok; +do_error_1(multi_apply_last) -> + erlang:apply(id(erlang), id(apply), [id(erlang), id(apply), [id(erlang), id(apply), [id(erlang), id(error), id([oops])]]]); +do_error_1(multi_apply) -> + erlang:apply(id(erlang), id(apply), [id(erlang), id(apply), [id(erlang), id(apply), [id(erlang), id(error), id([oops])]]]), + ok; +do_error_1(call_const_last) -> + erlang:error(oops); +do_error_1(call_last) -> + erlang:error(id(oops)); +do_error_1(call_const) -> + erlang:error(oops), + ok; +do_error_1(call) -> + erlang:error(id(oops)). + + %% Helpers |