1: %%
    2: %% %CopyrightBegin%
    3: %%
    4: %% Copyright Ericsson AB 1999-2011. All Rights Reserved.
    5: %%
    6: %% The contents of this file are subject to the Erlang Public License,
    7: %% Version 1.1, (the "License"); you may not use this file except in
    8: %% compliance with the License. You should have received a copy of the
    9: %% Erlang Public License along with this software. If not, it can be
   10: %% retrieved online at http://www.erlang.org/.
   11: %%
   12: %% Software distributed under the License is distributed on an "AS IS"
   13: %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
   14: %% the License for the specific language governing rights and limitations
   15: %% under the License.
   16: %%
   17: %% %CopyrightEnd%
   18: %%
   19: 
   20: %%
   21: -module(exception_SUITE).
   22: 
   23: -export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2,
   24: 	 init_per_testcase/2,end_per_testcase/2,
   25: 	 init_per_suite/1,end_per_suite/1,
   26: 	 badmatch/1,pending_errors/1,nil_arith/1,
   27:          stacktrace/1,nested_stacktrace/1,raise/1,gunilla/1,per/1]).
   28: 
   29: -export([bad_guy/2]).
   30: 
   31: -include_lib("test_server/include/test_server.hrl").
   32: 
   33: suite() -> [{ct_hooks,[ts_install_cth]}].
   34: 
   35: %% Filler.
   36: %%
   37: %%
   38: %%
   39: %%
   40: %% This is line 40.
   41: even(N) when is_integer(N), N > 1, (N rem 2) == 0 ->
   42:     odd(N-1)++[N].
   43: 
   44: odd(N) when is_integer(N), N > 1, (N rem 2) == 1 ->
   45:     even(N-1)++[N].
   46: 
   47: 
   48: all() -> 
   49:     cases().
   50: 
   51: groups() -> 
   52:     [].
   53: 
   54: init_per_group(_GroupName, Config) ->
   55:     Config.
   56: 
   57: end_per_group(_GroupName, Config) ->
   58:     Config.
   59: 
   60: 
   61: cases() -> 
   62:     [badmatch, pending_errors, nil_arith, stacktrace,
   63:      nested_stacktrace, raise, gunilla, per].
   64: 
   65: -define(try_match(E),
   66: 	catch ?MODULE:bar(),
   67: 	{'EXIT', {{badmatch, nomatch}, _}} = (catch E = nomatch)).
   68: 
   69: init_per_testcase(_Case, Config) ->
   70:     test_lib:interpret(?MODULE),
   71:     Dog = test_server:timetrap(?t:minutes(1)),
   72:     [{watchdog,Dog}|Config].
   73: 
   74: end_per_testcase(_Case, Config) ->
   75:     Dog = ?config(watchdog, Config),
   76:     ?t:timetrap_cancel(Dog),
   77:     ok.
   78: 
   79: init_per_suite(Config) when is_list(Config) ->
   80:     ?line test_lib:interpret(?MODULE),
   81:     ?line true = lists:member(?MODULE, int:interpreted()),
   82:     Config.
   83: 
   84: end_per_suite(Config) when is_list(Config) ->
   85:     ok.
   86: 
   87: %% Test that deliberately bad matches are reported correctly.
   88: 
   89: badmatch(Config) when is_list(Config) ->
   90:     ?line ?try_match(a),
   91:     ?line ?try_match(42),
   92:     ?line ?try_match({a, b, c}),
   93:     ?line ?try_match([]),
   94:     ?line ?try_match(1.0),
   95:     ok.
   96: 
   97: %% Test various exceptions, in the presence of a previous error suppressed
   98: %% in a guard.
   99: pending_errors(Config) when is_list(Config) ->
  100:     ?line pending(e_badmatch, {badmatch, b}),
  101:     ?line pending(x, function_clause),
  102:     ?line pending(e_case, {case_clause, xxx}),
  103:     ?line pending(e_if, if_clause),
  104:     ?line pending(e_badarith, badarith),
  105:     ?line pending(e_undef, undef),
  106:     ?line pending(e_timeoutval, timeout_value),
  107:     ?line pending(e_badarg, badarg),
  108:     ?line pending(e_badarg_spawn, badarg),
  109:     ok.
  110: 
  111: bad_guy(pe_badarith, Other) when Other+1 == 0 -> % badarith (suppressed)
  112:     ok;
  113: bad_guy(pe_badarg, Other) when length(Other) > 0 -> % badarg (suppressed)
  114:     ok;
  115: bad_guy(_, e_case) ->
  116:     case id(xxx) of
  117: 	ok -> ok
  118:     end;					% case_clause
  119: bad_guy(_, e_if) ->
  120:     if
  121: 	a == b -> ok
  122:     end;					% if_clause
  123: bad_guy(_, e_badarith) ->
  124:     1+b;					% badarith
  125: bad_guy(_, e_undef) ->
  126:     non_existing_module:foo();			% undef
  127: bad_guy(_, e_timeoutval) ->
  128:     receive
  129: 	after arne ->				% timeout_value
  130: 		ok
  131: 	end;
  132: bad_guy(_, e_badarg) ->
  133:     node(xxx);					% badarg
  134: bad_guy(_, e_badarg_spawn) ->
  135:     spawn({}, {}, {});				% badarg
  136: bad_guy(_, e_badmatch) ->
  137:     a = id(b).					% badmatch
  138: 
  139: pending(Arg, Expected) ->
  140:     pending(pe_badarith, Arg, Expected),
  141:     pending(pe_badarg, Arg, Expected).
  142: 
  143: pending(First, Second, Expected) ->
  144:     pending_catched(First, Second, Expected),
  145:     pending_exit_message([First, Second], Expected).
  146: 
  147: pending_catched(First, Second, Expected) ->
  148:     ok = io:format("Catching bad_guy(~p, ~p)", [First, Second]),
  149:     case catch bad_guy(First, Second) of
  150: 	{'EXIT', Reason} ->
  151: 	    pending(Reason, bad_guy, [First, Second], Expected);
  152: 	Other ->
  153: 	    test_server:fail({not_exit, Other})
  154:     end.
  155: 
  156: pending_exit_message(Args, Expected) ->
  157:     ok = io:format("Trapping EXITs from spawn_link(~p, ~p, ~p)",
  158: 		   [?MODULE, bad_guy, Args]),
  159:     process_flag(trap_exit, true),
  160:     Pid = spawn_link(?MODULE, bad_guy, Args),
  161:     receive
  162: 	{'EXIT', Pid, Reason} ->
  163: 	    pending(Reason, bad_guy, Args, Expected);
  164: 	Other ->
  165: 	    test_server:fail({unexpected_message, Other})
  166:     after 10000 ->
  167: 	    test_server:fail(timeout)
  168:     end,
  169:     process_flag(trap_exit, false).
  170: 
  171: pending({badarg, [{erlang,Bif,BifArgs,_},{?MODULE,Func,Arity,_}|_]},
  172: 	Func, Args, _Code)
  173:   when is_atom(Bif), is_list(BifArgs), length(Args) == Arity ->
  174:     ok;
  175: pending({undef,[{non_existing_module,foo,[],_}|_]}, _, _, _) ->
  176:     ok;
  177: pending({function_clause,[{?MODULE,Func,Args,_}|_]}, Func, Args, _Code) ->
  178:     ok;
  179: pending({Code,[{?MODULE,Func,Arity,_}|_]}, Func, Args, Code)
  180:   when length(Args) == Arity ->
  181:     ok;
  182: pending(Reason, _Function, _Args, _Code) ->
  183:     test_server:fail({bad_exit_reason,Reason}).
  184: 
  185: %% Test that doing arithmetics on [] gives a badarith EXIT and not a crash.
  186: 
  187: nil_arith(Config) when is_list(Config) ->
  188:     ?line ba_plus_minus_times([], []),
  189: 
  190:     ?line ba_plus_minus_times([], 0),
  191:     ?line ba_plus_minus_times([], 42),
  192:     ?line ba_plus_minus_times([], 38724978123478923784),
  193:     ?line ba_plus_minus_times([], 38.72),
  194: 
  195:     ?line ba_plus_minus_times(0, []),
  196:     ?line ba_plus_minus_times(334, []),
  197:     ?line ba_plus_minus_times(387249797813478923784, []),
  198:     ?line ba_plus_minus_times(344.22, []),
  199: 
  200:     ?line ba_div_rem([], []),
  201: 
  202:     ?line ba_div_rem([], 0),
  203:     ?line ba_div_rem([], 1),
  204:     ?line ba_div_rem([], 42),
  205:     ?line ba_div_rem([], 38724978123478923784),
  206:     ?line ba_div_rem(344.22, []),
  207: 
  208:     ?line ba_div_rem(0, []),
  209:     ?line ba_div_rem(1, []),
  210:     ?line ba_div_rem(334, []),
  211:     ?line ba_div_rem(387249797813478923784, []),
  212:     ?line ba_div_rem(344.22, []),
  213: 
  214:     ?line ba_div_rem(344.22, 0.0),
  215:     ?line ba_div_rem(1, 0.0),
  216:     ?line ba_div_rem(392873498733971, 0.0),
  217: 
  218:     ?line ba_bop([], []),
  219:     ?line ba_bop(0, []),
  220:     ?line ba_bop(42, []),
  221:     ?line ba_bop(-42342742987343, []),
  222:     ?line ba_bop(238.342, []),
  223:     ?line ba_bop([], 0),
  224:     ?line ba_bop([], -243),
  225:     ?line ba_bop([], 243),
  226:     ?line ba_bop([], 2438724982478933),
  227:     ?line ba_bop([], 3987.37),
  228: 
  229:     ?line ba_bnot([]),
  230:     ?line ba_bnot(23.33),
  231: 
  232:     ?line ba_shift([], []),
  233:     ?line ba_shift([], 0),
  234:     ?line ba_shift([], 4),
  235:     ?line ba_shift([], -4),
  236:     ?line ba_shift([], 2343333333333),
  237:     ?line ba_shift([], -333333333),
  238:     ?line ba_shift([], 234.00),
  239:     ?line ba_shift(23, []),
  240:     ?line ba_shift(0, []),
  241:     ?line ba_shift(-3433443433433323, []),
  242:     ?line ba_shift(433443433433323, []),
  243:     ?line ba_shift(343.93, []),
  244:     ok.
  245: 
  246: ba_plus_minus_times(A, B) ->
  247:     io:format("~p + ~p", [A, B]),
  248:     {'EXIT', {badarith, _}} = (catch A + B),
  249:     io:format("~p - ~p", [A, B]),
  250:     {'EXIT', {badarith, _}} = (catch A - B),
  251:     io:format("~p * ~p", [A, B]),
  252:     {'EXIT', {badarith, _}} = (catch A * B).
  253: 
  254: ba_div_rem(A, B) ->
  255:     io:format("~p / ~p", [A, B]),
  256:     {'EXIT', {badarith, _}} = (catch A / B),
  257:     io:format("~p div ~p", [A, B]),
  258:     {'EXIT', {badarith, _}} = (catch A div B),
  259:     io:format("~p rem ~p", [A, B]),
  260:     {'EXIT', {badarith, _}} = (catch A rem B).
  261: 
  262: ba_bop(A, B) ->
  263:     io:format("~p band ~p", [A, B]),
  264:     {'EXIT', {badarith, _}} = (catch A band B),
  265:     io:format("~p bor ~p", [A, B]),
  266:     {'EXIT', {badarith, _}} = (catch A bor B),
  267:     io:format("~p bxor ~p", [A, B]),
  268:     {'EXIT', {badarith, _}} = (catch A bxor B).
  269: 
  270: ba_shift(A, B) ->
  271:     io:format("~p bsl ~p", [A, B]),
  272:     {'EXIT', {badarith, _}} = (catch A bsl B),
  273:     io:format("~p bsr ~p", [A, B]),
  274:     {'EXIT', {badarith, _}} = (catch A bsr B).
  275: 
  276: ba_bnot(A) ->
  277:     io:format("bnot ~p", [A]),
  278:     {'EXIT', {badarith, _}} = (catch bnot A).
  279: 
  280: stacktrace(Conf) when is_list(Conf) ->
  281:     Tag = make_ref(),
  282:     ?line {_,Mref} = spawn_monitor(fun() -> exit({Tag,erlang:get_stacktrace()}) end),
  283:     ?line {Tag,[]} = receive {'DOWN',Mref,_,_,Info} -> Info end,
  284:     V = [make_ref()|self()],
  285:     ?line {value2,{caught1,badarg,[{erlang,abs,[V],_}|_]=St1}} =
  286: 	stacktrace_1({'abs',V}, error, {value,V}),
  287:     ?line St1 = erase(stacktrace1),
  288:     ?line St1 = erase(stacktrace2),
  289:     ?line St1 = erlang:get_stacktrace(),
  290:     ?line {caught2,{error,badarith},[{?MODULE,my_add,2,_}|_]=St2} =
  291: 	stacktrace_1({'div',{1,0}}, error, {'add',{0,a}}),
  292:     ?line [{?MODULE,my_div,2,_}|_] = erase(stacktrace1),
  293:     ?line St2 = erase(stacktrace2),
  294:     ?line St2 = erlang:get_stacktrace(),
  295:     ?line {caught2,{error,{try_clause,V}},[{?MODULE,stacktrace_1,3,_}|_]=St3} =
  296: 	stacktrace_1({value,V}, error, {value,V}),
  297:     ?line St3 = erase(stacktrace1),
  298:     ?line St3 = erase(stacktrace2),
  299:     ?line St3 = erlang:get_stacktrace(),
  300:     ?line {caught2,{throw,V},[{?MODULE,foo,1,_}|_]=St4} =
  301: 	stacktrace_1({value,V}, error, {throw,V}),
  302:     ?line [{?MODULE,stacktrace_1,3,_}|_] = erase(stacktrace1),
  303:     ?line St4 = erase(stacktrace2),
  304:     ?line St4 = erlang:get_stacktrace(),
  305:     ok.
  306: 
  307: stacktrace_1(X, C1, Y) ->
  308:     erase(stacktrace1),
  309:     erase(stacktrace2),
  310:     try try foo(X) of
  311:             C1 -> value1
  312:         catch
  313:             C1:D1 -> {caught1,D1,erlang:get_stacktrace()}
  314:         after
  315:             put(stacktrace1, erlang:get_stacktrace()),
  316: 	    foo(Y)
  317:         end of
  318:         V2 -> {value2,V2}
  319:     catch
  320:         C2:D2 -> {caught2,{C2,D2},erlang:get_stacktrace()}
  321:     after
  322:         put(stacktrace2, erlang:get_stacktrace())
  323:     end.
  324: 
  325: 
  326: 
  327: nested_stacktrace(Conf) when is_list(Conf) ->
  328:     V = [{make_ref()}|[self()]],
  329:     ?line value1 =
  330: 	nested_stacktrace_1({{value,{V,x1}},void,{V,x1}},
  331: 			    {void,void,void}),
  332:     ?line {caught1,
  333: 	   [{?MODULE,my_add,2,_}|_],
  334: 	   value2,
  335: 	   [{?MODULE,my_add,2,_}|_]} =
  336: 	nested_stacktrace_1({{'add',{V,x1}},error,badarith},
  337: 			    {{value,{V,x2}},void,{V,x2}}),
  338:     ?line {caught1,
  339: 	   [{?MODULE,my_add,2,_}|_],
  340: 	   {caught2,[{erlang,abs,[V],_}|_]},
  341: 	   [{erlang,abs,[V],_}|_]} =
  342: 	nested_stacktrace_1({{'add',{V,x1}},error,badarith},
  343: 			    {{'abs',V},error,badarg}),
  344:     ok.
  345: 
  346: nested_stacktrace_1({X1,C1,V1}, {X2,C2,V2}) ->
  347:     try foo(X1) of
  348:         V1 -> value1
  349:     catch
  350:         C1:V1 ->
  351: 	    S1 = erlang:get_stacktrace(),
  352:             T2 =
  353:                 try foo(X2) of
  354: 	            V2 -> value2
  355:                 catch
  356:                     C2:V2 -> {caught2,erlang:get_stacktrace()}
  357:                 end,
  358:             {caught1,S1,T2,erlang:get_stacktrace()}
  359:     end.
  360: 
  361: 
  362: 
  363: raise(Conf) when is_list(Conf) ->
  364:     ?line erase(raise),
  365:     ?line A =
  366: 	try
  367: 	    ?line try foo({'div',{1,0}})
  368: 		  catch
  369: 		      error:badarith ->
  370: 			  put(raise, A0 = erlang:get_stacktrace()),
  371: 			  ?line erlang:raise(error, badarith, A0)
  372: 		  end
  373: 	catch
  374: 	    error:badarith ->
  375: 		?line A1 = erlang:get_stacktrace(),
  376: 		?line A1 = get(raise)
  377: 	end,
  378:     ?line A = erlang:get_stacktrace(),
  379:     ?line A = get(raise),
  380:     ?line [{?MODULE,my_div,2,_}|_] = A,
  381:     %%
  382:     N = 8, % Must be even
  383:     ?line N = erlang:system_flag(backtrace_depth, N),
  384:     ?line try even(N)
  385: 	  catch error:function_clause -> ok
  386: 	  end,
  387:     ?line B = odd_even(N, []),
  388:     ?line B = erlang:get_stacktrace(),
  389:     %%
  390:     ?line C0 = odd_even(N+1, []),
  391:     ?line C = lists:sublist(C0, N),
  392:     ?line try odd(N+1)
  393: 	  catch error:function_clause -> ok
  394: 	  end,
  395:     ?line C = erlang:get_stacktrace(),
  396:     ?line try erlang:raise(error, function_clause, C0)
  397:           catch error:function_clause -> ok
  398:           end,
  399:     ?line C = erlang:get_stacktrace(),
  400:     ok.
  401: 
  402: odd_even(N, R) when is_integer(N), N > 1 ->
  403:     odd_even(N-1,
  404: 	     [if (N rem 2) == 0 ->
  405: 		      {?MODULE,even,1,[{file,?MODULE_STRING++".erl"},
  406: 				       {line,42}]};
  407: 		 true ->
  408: 		      {?MODULE,odd,1,[{file,?MODULE_STRING++".erl"},
  409: 				      {line,45}]}
  410: 	      end|R]);
  411: odd_even(1, R) ->
  412:     [{?MODULE,odd,[1],[{file,?MODULE_STRING++".erl"},
  413: 		       {line,44}]}|R].
  414: 
  415: foo({value,Value}) -> Value;
  416: foo({'div',{A,B}}) ->
  417:     my_div(A, B);
  418: foo({'add',{A,B}}) ->
  419:     my_add(A, B);
  420: foo({'abs',X}) ->
  421:     my_abs(X);
  422: foo({error,Error}) ->
  423:     erlang:error(Error);
  424: foo({throw,Throw}) ->
  425:     erlang:throw(Throw);
  426: foo({exit,Exit}) ->
  427:     erlang:exit(Exit);
  428: foo({raise,{Class,Reason,Stacktrace}}) ->
  429:     erlang:raise(Class, Reason, Stacktrace).
  430: %%foo(function_clause) -> % must not be defined!
  431: 
  432: my_div(A, B) ->
  433:     A div B.
  434: 
  435: my_add(A, B) ->
  436:     A + B.
  437: 
  438: my_abs(X) -> abs(X).
  439: 
  440: gunilla(Config) when is_list(Config) ->
  441:     ?line {throw,kalle} = gunilla_1(),
  442:     ?line [] = erlang:get_stacktrace(),
  443:     ok.
  444: 
  445: gunilla_1() ->
  446:     try try arne()
  447: 	after
  448: 	    pelle
  449: 	end
  450:     catch
  451: 	C:R ->
  452: 	    {C,R}
  453:     end.
  454: 
  455: arne() ->
  456:     %% Empty stack trace used to cause change the error class to 'error'.
  457:     erlang:raise(throw, kalle, []).
  458: 
  459: per(Config) when is_list(Config) ->
  460:     try
  461: 	t1(0,pad,0),
  462: 	t2(0,pad,0)
  463:     catch
  464: 	error:badarith ->
  465: 	    ok
  466:     end.
  467: 
  468: t1(_,X,_) ->
  469:    (1 bsl X) + 1.
  470: 
  471: t2(_,X,_) ->
  472:    (X bsl 1) + 1.
  473: 
  474: id(I) -> I.