1: %%
    2: %% %CopyrightBegin%
    3: %%
    4: %% Copyright Ericsson AB 2003-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: -module(ms_transform_SUITE).
   20: -author('pan@erix.ericsson.se').
   21: 
   22: -include_lib("test_server/include/test_server.hrl").
   23: 
   24: -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, 
   25: 	 init_per_group/2,end_per_group/2]).
   26: -export([basic_ets/1]).
   27: -export([basic_dbg/1]).
   28: -export([from_shell/1]).
   29: -export([records/1]).
   30: -export([record_index/1]).
   31: -export([multipass/1]).
   32: -export([top_match/1]).
   33: -export([old_guards/1]).
   34: -export([autoimported/1]).
   35: -export([semicolon/1]).
   36: -export([bitsyntax/1]).
   37: -export([record_defaults/1]).
   38: -export([andalso_orelse/1]).
   39: -export([float_1_function/1]).
   40: -export([action_function/1]).
   41: -export([warnings/1]).
   42: -export([no_warnings/1]).
   43: -export([init_per_testcase/2, end_per_testcase/2]).
   44: 
   45: init_per_testcase(_Func, Config) ->
   46:     Dog=test_server:timetrap(test_server:seconds(360)),
   47:     [{watchdog, Dog}|Config].
   48: 
   49: end_per_testcase(_Func, Config) ->
   50:     Dog=?config(watchdog, Config),
   51:     test_server:timetrap_cancel(Dog).
   52: 
   53: suite() -> [{ct_hooks,[ts_install_cth]}].
   54: 
   55: all() -> 
   56:     [from_shell, basic_ets, basic_dbg, records,
   57:      record_index, multipass, bitsyntax, record_defaults,
   58:      andalso_orelse, float_1_function, action_function,
   59:      warnings, no_warnings, top_match, old_guards, autoimported,
   60:      semicolon].
   61: 
   62: groups() -> 
   63:     [].
   64: 
   65: init_per_suite(Config) ->
   66:     Config.
   67: 
   68: end_per_suite(_Config) ->
   69:     ok.
   70: 
   71: init_per_group(_GroupName, Config) ->
   72:     Config.
   73: 
   74: end_per_group(_GroupName, Config) ->
   75:     Config.
   76: 
   77: 
   78: %% This may be subject to change
   79: -define(WARN_NUMBER_SHADOW,50).
   80: warnings(suite) ->
   81:     [];
   82: warnings(doc) ->
   83:     ["Check that shadowed variables in fun head generate warning"];
   84: warnings(Config) when is_list(Config) ->
   85:     ?line setup(Config),
   86:     Prog = <<"A=5, "
   87: 	    "ets:fun2ms(fun({A,B}) "
   88: 	    "            when is_integer(A) and (A+5 > B) -> "
   89: 	    "              A andalso B "
   90: 	    "            end)">>,
   91:     ?line [{_,[{_,ms_transform,{?WARN_NUMBER_SHADOW,'A'}}]}] =
   92: 	compile_ww(Prog),
   93:     Prog2 = <<"C=5, "
   94: 	    "ets:fun2ms(fun({A,B} = C) "
   95: 	    "            when is_integer(A) and (A+5 > B) -> "
   96: 	    "              {A andalso B,C} "
   97: 	    "            end)">>,
   98:     ?line [{_,[{_,ms_transform,{?WARN_NUMBER_SHADOW,'C'}}]}] =
   99: 	compile_ww(Prog2),
  100:     Rec3 = <<"-record(a,{a,b,c,d=foppa}).">>,
  101:     Prog3 = <<"A=3,C=5, "
  102: 	    "ets:fun2ms(fun(#a{a = A, b = B} = C) "
  103: 	    "            when is_integer(A) and (A+5 > B) -> "
  104: 	    "              {A andalso B,C} "
  105: 	    "            end)">>,
  106:     ?line [{_,[{_,ms_transform,{?WARN_NUMBER_SHADOW,'A'}},
  107: 	       {_,ms_transform,{?WARN_NUMBER_SHADOW,'C'}}]}] =
  108: 	compile_ww(Rec3,Prog3),
  109:     Rec4 = <<"-record(a,{a,b,c,d=foppa}).">>,
  110:     Prog4 = <<"A=3,C=5, "
  111: 	     "F = fun(B) -> B*3 end,"
  112: 	     "erlang:display(F(A)),"
  113: 	     "ets:fun2ms(fun(#a{a = A, b = B} = C) "
  114: 	     "            when is_integer(A) and (A+5 > B) -> "
  115: 	     "              {A andalso B,C} "
  116: 	     "            end)">>,
  117:     ?line [{_,[{_,ms_transform,{?WARN_NUMBER_SHADOW,'A'}},
  118: 	       {_,ms_transform,{?WARN_NUMBER_SHADOW,'C'}}]}] =
  119: 	compile_ww(Rec4,Prog4),
  120:     Rec5 = <<"-record(a,{a,b,c,d=foppa}).">>,
  121:     Prog5 = <<"A=3,C=5, "
  122: 	     "F = fun(B) -> B*3 end,"
  123: 	     "erlang:display(F(A)),"
  124: 	     "B = ets:fun2ms(fun(#a{a = A, b = B} = C) "
  125: 	     "            when is_integer(A) and (A+5 > B) -> "
  126: 	     "              {A andalso B,C} "
  127: 	     "            end)">>,
  128:     ?line [{_,[{_,ms_transform,{?WARN_NUMBER_SHADOW,'A'}},
  129: 	       {_,ms_transform,{?WARN_NUMBER_SHADOW,'C'}}]}] =
  130: 	compile_ww(Rec5,Prog5),
  131:     Prog6 = <<"   X=bar, "
  132: 	     "    A = case X of"
  133: 	     "       foo ->"
  134: 	     "          foo;"
  135: 	     "       Y ->"
  136: 	     "          ets:fun2ms(fun(Y) ->" % This is a warning
  137: 	     "                         3*Y"
  138: 	     "                     end)"
  139: 	     "   end,"
  140: 	     "   ets:fun2ms(fun(Y) ->" % Y out of "scope" here, so no warning
  141: 	     "                  {3*Y,A}"
  142: 	     "              end)">>,
  143:     ?line [{_,[{_,ms_transform,{?WARN_NUMBER_SHADOW,'Y'}}]}] =
  144: 	compile_ww(Prog6),
  145:     Prog7 = <<"   X=bar, "
  146: 	     "    A = case X of"
  147: 	     "       foo ->"
  148: 	     "          Y = foo;"
  149: 	     "       Y ->"
  150: 	     "          bar"
  151: 	     "   end,"
  152: 	     "   ets:fun2ms(fun(Y) ->" % Y exported from case and safe, so warn
  153: 	     "                  {3*Y,A}"
  154: 	     "              end)">>,
  155:     ?line [{_,[{_,ms_transform,{?WARN_NUMBER_SHADOW,'Y'}}]}] =
  156: 	compile_ww(Prog7),
  157:     ok.
  158: 
  159: no_warnings(suite) ->
  160:     [];
  161: no_warnings(doc) ->
  162:     ["Check that variables bound in other function clauses don't generate "
  163:      "warning"];
  164: no_warnings(Config) when is_list(Config) ->
  165:     ?line setup(Config),
  166:     Prog = <<"tmp(X) when X > 100 ->\n",
  167: 	     "   Y=X,\n"
  168: 	     "   Y;\n"
  169: 	     "tmp(X) ->\n"
  170: 	     "   ets:fun2ms(fun(Y) ->\n"
  171: 	     "                  {X, 3*Y}\n"
  172: 	     "              end)">>,
  173:     ?line [] = compile_no_ww(Prog),
  174: 
  175:     Prog2 = <<"tmp(X) when X > 100 ->\n",
  176: 	     "   Y=X,\n"
  177: 	     "   Y;\n"
  178: 	     "tmp(X) when X < 200 ->\n"
  179: 	     "   ok;\n"
  180: 	     "tmp(X) ->\n"
  181: 	     "   ets:fun2ms(fun(Y) ->\n"
  182: 	     "                  {X, 3*Y}\n"
  183: 	     "              end)">>,
  184:     ?line [] = compile_no_ww(Prog2),
  185:     ok.
  186: 
  187: andalso_orelse(suite) ->
  188:     [];
  189: andalso_orelse(doc) ->
  190:     ["Tests that andalso and orelse are allowed in guards."];
  191: andalso_orelse(Config) when is_list(Config) ->
  192:     ?line setup(Config),
  193:     ?line [{{'$1','$2'},
  194: 	    [{'and',{is_integer,'$1'},{'>',{'+','$1',5},'$2'}}],
  195: 	    [{'andalso','$1','$2'}]}] = 
  196: 	compile_and_run(<<"ets:fun2ms(fun({A,B}) "
  197: 			 "            when is_integer(A) and (A+5 > B) -> "
  198: 			 "              A andalso B "
  199: 			 "            end)">>),
  200:     ?line [{{'$1','$2'},
  201: 	    [{'or',{is_atom,'$1'},{'>',{'+','$1',5},'$2'}}],
  202: 	    [{'orelse','$1','$2'}]}] = 
  203: 	compile_and_run(<<"ets:fun2ms(fun({A,B}) "
  204: 			 "            when is_atom(A) or (A+5 > B) -> "
  205: 			 "              A orelse B "
  206: 			 "            end)">>),
  207:     ?line [{{'$1','$2'},
  208:             [{'andalso',{is_integer,'$1'},{'>',{'+','$1',5},'$2'}}],
  209:             ['$1']}] = 
  210:         compile_and_run(
  211: 	    <<"ets:fun2ms(fun({A,B}) when is_integer(A) andalso (A+5 > B) ->"
  212: 	     "			 A "
  213: 	     "		 end)">>),
  214:     ?line [{{'$1','$2'},
  215:             [{'orelse',{is_atom,'$1'},{'>',{'+','$1',5},'$2'}}],
  216:             ['$1']}] =
  217:         compile_and_run(
  218: 	    <<"ets:fun2ms(fun({A,B}) when is_atom(A) orelse (A+5 > B) -> "
  219: 	     "			 A "
  220: 	     "		 end)">>),
  221:     ok.
  222:     
  223:     
  224: bitsyntax(suite) ->
  225:     [];
  226: bitsyntax(doc) ->
  227:     ["Tests that bitsyntax works and does not work where appropriate"];
  228: bitsyntax(Config) when is_list(Config) ->
  229:     ?line setup(Config),
  230:     ?line [{'_',[],
  231: 	    [<<0,27,0,27>>]}] =
  232: 	compile_and_run(<<"A = 27, "
  233: 			  "ets:fun2ms(fun(_) -> <<A:16,27:16>> end)">>),
  234:     ?line  [{{<<15,47>>,
  235: 	      '$1',
  236: 	      '$2'},
  237: 	     [{'=:=','$1',
  238: 	       <<0,27>>},
  239: 	      {'=:=','$2',
  240: 	       <<27,28,19>>}],
  241: 	     [<<188,0,13>>]}] =
  242: 	compile_and_run(<<"A = 27, "
  243: 			  "ets:fun2ms("
  244:                           "  fun({<<15,47>>,B,C}) "
  245: 			  "  when B =:= <<A:16>>, C =:= <<27,28,19>> -> "
  246: 			  "    <<A:4,12:4,13:16>> "
  247: 			  "  end)">>),
  248:     ?line expect_failure(
  249: 	    <<>>,
  250: 	    <<"ets:fun2ms(fun({<<15,47>>,B,C}) "
  251: 	     "            when B =:= <<16>>, C =:= <<27,28,19>> -> "
  252: 	     "              <<B:4,12:4,13:16>> "
  253: 	     "            end)">>),
  254:     ?line expect_failure(
  255: 	    <<>>,
  256: 	    <<"ets:fun2ms(fun({<<A:15,47>>,B,C}) "
  257: 	     "            when B =:= <<16>>, C =:= <<27,28,19>> -> "
  258: 	     "              <<B:4,12:4,13:16>> "
  259: 	     "            end)">>),
  260:     ok.
  261: 
  262: record_defaults(suite) ->
  263:     [];
  264: record_defaults(doc) ->
  265:     ["Tests that record defaults works"];
  266: record_defaults(Config) when is_list(Config) ->
  267:     ?line setup(Config),    
  268:     ?line [{{<<27>>,{a,5,'$1',hej,hej}},
  269: 	    [],
  270: 	    [{{a,hej,{'*','$1',2},flurp,flurp}}]}] = 
  271: 	compile_and_run(<<"-record(a,{a,b,c,d=foppa}).">>,
  272: 			<<"ets:fun2ms(fun({<<27>>,#a{a=5, b=B,_=hej}}) -> "
  273: 		                        "#a{a=hej,b=B*2,_=flurp} "
  274: 	                              "end)">>),
  275:     ok.
  276: 
  277: basic_ets(suite) ->
  278:     [];
  279: basic_ets(doc) ->
  280:     ["Tests basic ets:fun2ms"];
  281: basic_ets(Config) when is_list(Config) ->
  282:     ?line setup(Config),
  283:     ?line [{{a,b},[],[true]}] = compile_and_run(
  284: 				  <<"ets:fun2ms(fun({a,b}) -> true end)">>),
  285:     ?line [{{'$1',foo},[{is_list,'$1'}],[{{{hd,'$1'},'$_'}}]},
  286:      {{'$1','$1'},[{is_tuple,'$1'}],[{{{element,1,'$1'},'$*'}}]}] =
  287: 	compile_and_run(<<"ets:fun2ms(fun({X,foo}) when is_list(X) -> ",
  288: 			                     "{hd(X),object()};",
  289: 			                "({X,X}) when is_tuple(X) ->",
  290: 			                     "{element(1,X),bindings()}",
  291: 			             "end)">>),
  292:     ?line [{{'$1','$2'},[],[{{'$2','$1'}}]}] =
  293: 	compile_and_run(<<"ets:fun2ms(fun({A,B}) -> {B,A} end)">>),
  294:     ?line [{{'$1','$2'},[],[['$2','$1']]}] =
  295: 	compile_and_run(<<"ets:fun2ms(fun({A,B}) -> [B,A] end)">>),
  296:     ok.
  297: 
  298: basic_dbg(suite) ->
  299:     [];
  300: basic_dbg(doc) ->
  301:     ["Tests basic ets:fun2ms"];
  302: basic_dbg(Config) when is_list(Config) ->
  303:     ?line setup(Config),
  304:     ?line [{[a,b],[],[{message,banan},{return_trace}]}] =
  305: 	compile_and_run(<<"dbg:fun2ms(fun([a,b]) -> message(banan), ",
  306: 			"return_trace() end)">>),
  307:     ?line [{['$1','$2'],[],[{{'$2','$1'}}]}] = 
  308: 	compile_and_run(<<"dbg:fun2ms(fun([A,B]) -> {B,A} end)">>),
  309:     ?line [{['$1','$2'],[],[['$2','$1']]}] =
  310: 	compile_and_run(<<"dbg:fun2ms(fun([A,B]) -> [B,A] end)">>),
  311:     ?line [{['$1','$2'],[],['$*']}] =
  312: 	compile_and_run(<<"dbg:fun2ms(fun([A,B]) -> bindings() end)">>),
  313:     ?line [{['$1','$2'],[],['$_']}] =
  314: 	compile_and_run(<<"dbg:fun2ms(fun([A,B]) -> object() end)">>),
  315:     ok.
  316: 
  317: from_shell(suite) ->
  318:     [];
  319: from_shell(doc) ->
  320:     ["Test calling of ets/dbg:fun2ms from the shell"]; 
  321: from_shell(Config) when is_list(Config) ->
  322:     ?line setup(Config),
  323:     ?line Fun = do_eval("fun({a,b}) -> true end"),
  324:     ?line [{{a,b},[],[true]}] = apply(ets,fun2ms,[Fun]),
  325:     ?line [{{a,b},[],[true]}] = do_eval("ets:fun2ms(fun({a,b}) -> true end)"),
  326:     ?line Fun2 = do_eval("fun([a,b]) -> message(banan), return_trace() end"),
  327:     ?line [{[a,b],[],[{message,banan},{return_trace}]}]
  328: 	= apply(dbg,fun2ms,[Fun2]),
  329:     ?line [{[a,b],[],[{message,banan},{return_trace}]}] =
  330: 	do_eval(
  331: 	  "dbg:fun2ms(fun([a,b]) -> message(banan), return_trace() end)"),
  332:     ok.
  333: 
  334: records(suite) ->
  335:     [];
  336: records(doc) ->
  337:     ["Tests expansion of records in fun2ms"];
  338: records(Config) when is_list(Config) ->
  339:     ?line setup(Config),
  340:     ?line RD = <<"-record(t, {"
  341: 	             "t1 = [],"
  342: 	             "t2 = foo,"
  343: 	             "t3,"
  344: 	             "t4"
  345: 	            "}).">>,
  346:     ?line [{{t,'$1','$2',foo,'_'},[{is_list,'$1'}],[{{{hd,'$1'},'$_'}}]},
  347:      {{t,'_','_','_','_'},[{'==',{element,2,'$_'},nisse}],[{{'$*'}}]}] =
  348: 	compile_and_run(RD,<<
  349:    "ets:fun2ms(fun(#t{t1 = X, t2 = Y, t3 = foo}) when is_list(X) -> 
  350:  		       {hd(X),object()}; 
  351:  		  (#t{}) when (object())#t.t1 == nisse -> 
  352:  		       {bindings()}  
  353:  	       end)">>),
  354:     ?line [{{t,'$1','$2','_',foo},
  355:       [{'==',{element,4,'$_'},7},{is_list,'$1'}],
  356:       [{{{hd,'$1'},'$_'}}]},
  357:      {'$1',[{is_record,'$1',t,5}],
  358:       [{{{element,2,'$1'},
  359: 	 {{t,'$1',foo,undefined,undefined}},
  360: 	 {{t,{element,2,'$1'},{element,3,'$1'},{element,4,'$1'},boooo}}}}]}] =
  361: 	compile_and_run(RD,<<
  362:     "ets:fun2ms(fun(#t{t1 = X, t2 = Y, t4 = foo}) when 
  363: 			 (object())#t.t3==7,is_list(X) -> 
  364:  		       {hd(X),object()}; 
  365:  		  (A) when is_record(A,t) -> 
  366:  		       {A#t.t1
  367: 			,#t{t1=A}
  368: 			,A#t{t4=boooo}
  369: 		       }  
  370:  	       end)"
  371: 			>>),
  372:     ?line [{[{t,'$1','$2',foo,'_'}],[{is_list,'$1'}],[{{{hd,'$1'},'$_'}}]},
  373:      {[{t,'_','_','_','_'}],[{'==',{element,2,{hd,'$_'}},nisse}],[{{'$*'}}]}]=
  374: 	compile_and_run(RD,<<
  375:     "dbg:fun2ms(fun([#t{t1 = X, t2 = Y, t3 = foo}]) when is_list(X) -> 
  376:  		       {hd(X),object()}; 
  377:  		  ([#t{}]) when (hd(object()))#t.t1 == nisse -> 
  378:  		       {bindings()}  
  379:  	       end)"
  380: 			>>),
  381:     ok.
  382: 
  383: 
  384: record_index(suite) ->
  385:     [];
  386: record_index(doc) ->
  387:     ["Tests expansion of records in fun2ms, part 2"];
  388: record_index(Config) when is_list(Config) ->
  389:     ?line setup(Config),
  390:     ?line RD = <<"-record(a,{a,b}).">>,
  391:     ?line [{{2},[],[true]}] = compile_and_run(RD,
  392: 			  <<"ets:fun2ms(fun({#a.a}) -> true end)">>),
  393:     ?line [{{2},[],[2]}] = compile_and_run(RD,
  394: 			  <<"ets:fun2ms(fun({#a.a}) -> #a.a end)">>),
  395:     ?line [{{2,'$1'},[{'>','$1',2}],[2]}] = compile_and_run(RD,
  396: 		    <<"ets:fun2ms(fun({#a.a,A}) when A > #a.a -> #a.a end)">>),
  397:     ok.
  398: 
  399: top_match(suite) ->
  400:     [];
  401: top_match(doc) ->
  402:     ["Tests matching on top level in head to give alias for object()"];
  403: top_match(Config) when is_list(Config) ->
  404:     ?line setup(Config),
  405:     ?line RD = <<"-record(a,{a,b}).">>,
  406:     ?line [{{a,3,'_'},[],['$_']}] = 
  407: 	compile_and_run(RD,
  408: 			<<"ets:fun2ms(fun(A = #a{a=3}) -> A end)">>),
  409:     ?line [{{a,3,'_'},[],['$_']}] = 
  410: 	compile_and_run(RD,
  411: 			<<"ets:fun2ms(fun(#a{a=3} = A) -> A end)">>),
  412:     ?line [{[a,b],[],['$_']}] = 
  413: 	compile_and_run(RD,
  414: 			<<"dbg:fun2ms(fun(A = [a,b]) -> A end)">>),
  415:     ?line [{[a,b],[],['$_']}] = 
  416: 	compile_and_run(RD,
  417: 			<<"dbg:fun2ms(fun([a,b] = A) -> A end)">>),
  418:     ?line expect_failure(RD,
  419: 			 <<"ets:fun2ms(fun({a,A = {_,b}}) -> A end)">>),
  420:     ?line expect_failure(RD,
  421: 			 <<"dbg:fun2ms(fun([a,A = {_,b}]) -> A end)">>),
  422:     ?line expect_failure(RD,
  423: 			 <<"ets:fun2ms(fun(A#a{a = 2}) -> A end)">>),
  424:     ok.
  425: 
  426: multipass(suite) ->
  427:     [];
  428: multipass(doc) ->
  429:     ["Tests that multi-defined fields in records give errors."];
  430: multipass(Config) when is_list(Config) ->
  431:     ?line setup(Config),
  432:     ?line RD = <<"-record(a,{a,b}).">>,
  433:     ?line expect_failure(RD,<<"ets:fun2ms(fun(A) -> #a{a=2,a=3} end)">>), 
  434:     ?line expect_failure(RD,<<"ets:fun2ms(fun(A) -> A#a{a=2,a=3} end)">>),
  435:     ?line expect_failure(RD,<<"ets:fun2ms(fun(A) when A =:= #a{a=2,a=3} ->",
  436: 			 " true end)">>), 
  437:     ?line expect_failure(RD,<<"ets:fun2ms(fun({A,B})when A =:= B#a{a=2,a=3}->",
  438: 			 "true end)">>),
  439:     ?line expect_failure(RD,<<"ets:fun2ms(fun(#a{a=3,a=3}) -> true end)">>),
  440:     ?line compile_and_run(RD,<<"ets:fun2ms(fun(A) -> #a{a=2,b=3} end)">>), 
  441:     ?line compile_and_run(RD,<<"ets:fun2ms(fun(A) -> A#a{a=2,b=3} end)">>),
  442:     ?line compile_and_run(RD,<<"ets:fun2ms(fun(A) when A =:= #a{a=2,b=3} ->",
  443: 			 " true end)">>), 
  444:     ?line compile_and_run(RD,<<"ets:fun2ms(fun({A,B})when A=:= B#a{a=2,b=3}->",
  445: 			 "true end)">>),
  446:     ?line compile_and_run(RD,<<"ets:fun2ms(fun(#a{a=3,b=3}) -> true end)">>),
  447:     ok.
  448: 
  449: 
  450: old_guards(suite) ->
  451:     [];
  452: old_guards(doc) ->
  453:     ["Tests that old type tests in guards are translated"];
  454: old_guards(Config) when is_list(Config) ->
  455:     ?line setup(Config),
  456:     Tests = [
  457: 	     {atom,is_atom},
  458: 	     {float,is_float},
  459: 	     {integer,is_integer},
  460: 	     {list,is_list},
  461: 	     {number,is_number},
  462: 	     {pid,is_pid},
  463: 	     {port,is_port},
  464: 	     {reference,is_reference},
  465: 	     {tuple,is_tuple},
  466: 	     {binary,is_binary},
  467: 	     {function,is_function}],
  468:     ?line lists:foreach(
  469: 	    fun({Old,New}) ->
  470: 		    Bin = list_to_binary([<<"ets:fun2ms(fun(X) when ">>,
  471: 					  atom_to_list(Old),
  472: 					  <<"(X)  -> true end)">>]),
  473: 		    case compile_and_run(Bin) of
  474: 			[{'$1',[{New,'$1'}],[true]}] -> 
  475: 			    ok;
  476: 			_ ->
  477: 			    exit({bad_result_for, binary_to_list(Bin)})
  478: 		    end
  479: 	    end,
  480: 	    Tests),
  481:     ?line RD = <<"-record(a,{a,b}).">>,
  482:     ?line [{'$1',[{is_record,'$1',a,3}],[true]}] =
  483: 	compile_and_run(RD,
  484: 			<<"ets:fun2ms(fun(X) when record(X,a) -> true end)">>),
  485:     ?line expect_failure
  486: 	    (RD,
  487: 	     <<"ets:fun2ms(fun(X) when integer(X) and constant(X) -> "
  488: 	      "true end)">>),
  489:     ?line [{'$1',[{is_integer,'$1'},
  490: 		  {is_float,'$1'},
  491: 		  {is_atom,'$1'},
  492: 		  {is_list,'$1'},
  493: 		  {is_number,'$1'},
  494: 		  {is_pid,'$1'},
  495: 		  {is_port,'$1'},
  496: 		  {is_reference,'$1'},
  497: 		  {is_tuple,'$1'},
  498: 		  {is_binary,'$1'},
  499: 		  {is_record,'$1',a,3}],
  500: 	    [true]}] =
  501: 	compile_and_run(RD, <<
  502: 			     "ets:fun2ms(fun(X) when integer(X),"
  503: 			     "float(X), atom(X),"
  504: 			     "list(X), number(X), pid(X),"
  505: 			     "port(X), reference(X), tuple(X),"
  506: 			     "binary(X), record(X,a) -> true end)"
  507: 			     >>),
  508:     ok.
  509:     
  510: autoimported(suite) ->
  511:     [];
  512: autoimported(doc) ->
  513:     ["Tests use of autoimported bif's used like erlang:'+'(A,B) in guards"
  514:      " and body."];
  515: autoimported(Config) when is_list(Config) ->
  516:     ?line setup(Config),
  517:     Allowed = [
  518: 	       {abs,1},
  519: 	       {element,2},
  520: 	       {hd,1},
  521: 	       {length,1},
  522: 	       {node,0},
  523: 	       {node,1},
  524: 	       {round,1},
  525: 	       {size,1},
  526: 	       {tl,1},
  527: 	       {trunc,1},
  528: 	       {self,0},
  529:                %{float,1}, see float_1_function/1
  530: 	       {is_atom,1},
  531: 	       {is_float,1},
  532: 	       {is_integer,1},
  533: 	       {is_list,1},
  534: 	       {is_number,1},
  535: 	       {is_pid,1},
  536: 	       {is_port,1},
  537: 	       {is_reference,1},
  538: 	       {is_tuple,1},
  539: 	       {is_binary,1},
  540: 	       {is_function,1},
  541: 	       {is_record,2,magic},
  542: 	       {'and',2,infix},
  543: 	       {'or',2,infix},
  544: 	       {'xor',2,infix},
  545: 	       {'not',1},
  546: 	       %{'andalso',2,infix},
  547: 	       %{'orelse',2,infix},
  548: 	       {'+',1},
  549: 	       {'+',2,infix},
  550: 	       {'-',1},
  551: 	       {'-',2,infix},
  552: 	       {'*',2,infix},
  553: 	       {'/',2,infix},
  554: 	       {'div',2,infix},
  555: 	       {'rem',2,infix},
  556: 	       {'band',2,infix},
  557: 	       {'bor',2,infix},
  558: 	       {'bxor',2,infix},
  559: 	       {'bnot',1},
  560: 	       {'bsl',2,infix},
  561: 	       {'bsr',2,infix},
  562: 	       {'>',2,infix},
  563: 	       {'>=',2,infix},
  564: 	       {'<',2,infix},
  565: 	       {'=<',2,infix},
  566: 	       {'==',2,infix},
  567: 	       {'=:=',2,infix},
  568: 	       {'/=',2,infix},
  569: 	       {'=/=',2,infix}],
  570:     ?line RD = <<"-record(a,{a,b}).">>,
  571:     ?line lists:foreach(
  572: 	    fun({A,0}) ->
  573: 		    L = atom_to_list(A),
  574: 		    Bin1 = list_to_binary(
  575: 			     [
  576: 			      <<"ets:fun2ms(fun(X) when ">>,
  577: 			      L,<<"() -> ">>,
  578: 			      L,<<"() end)">>
  579: 			     ]),
  580: 		    Bin2 = list_to_binary(
  581: 			     [
  582: 			      <<"ets:fun2ms(fun(X) when erlang:'">>,
  583: 			      L,<<"'() -> erlang:'">>,
  584: 			      L,<<"'() end)">>
  585: 			     ]),
  586: 		    Res1 = compile_and_run(Bin1),
  587: 		    Res2 = compile_and_run(Bin2),
  588: 		    case Res1 =:= Res2 of
  589: 			true ->
  590: 			    ok;
  591: 			false ->
  592: 			    exit({not_equal,{Res1,Res2,A}})
  593: 		    end;
  594: 	    ({A,1}) ->
  595: 		    L = atom_to_list(A),
  596: 		    Bin1 = list_to_binary(
  597: 			     [
  598: 			      <<"ets:fun2ms(fun(X) when ">>,
  599: 			      L,<<"(X) -> ">>,
  600: 			      L,<<"(X) end)">>
  601: 			     ]),
  602: 		    Bin2 = list_to_binary(
  603: 			     [
  604: 			      <<"ets:fun2ms(fun(X) when erlang:'">>,
  605: 			      L,<<"'(X) -> erlang:'">>,
  606: 			      L,<<"'(X) end)">>
  607: 			     ]),
  608: 		    Res1 = compile_and_run(Bin1),
  609: 		    Res2 = compile_and_run(Bin2),
  610: 		    case Res1 =:= Res2 of
  611: 			true ->
  612: 			    ok;
  613: 			false ->
  614: 			    exit({not_equal,{Res1,Res2,A}})
  615: 		    end;
  616: 	    ({A,2}) ->
  617: 		    L = atom_to_list(A),
  618: 		    Bin1 = list_to_binary(
  619: 			     [
  620: 			      <<"ets:fun2ms(fun({X,Y}) when ">>,
  621: 			      L,<<"(X,Y) -> ">>,
  622: 			      L,<<"(X,Y) end)">>
  623: 			     ]),
  624: 		    Bin2 = list_to_binary(
  625: 			     [
  626: 			      <<"ets:fun2ms(fun({X,Y}) when erlang:'">>,
  627: 			      L,<<"'(X,Y) -> erlang:'">>,
  628: 			      L,<<"'(X,Y) end)">>
  629: 			     ]),
  630: 		    Res1 = compile_and_run(Bin1),
  631: 		    Res2 = compile_and_run(Bin2),
  632: 		    case Res1 =:= Res2 of
  633: 			true ->
  634: 			    ok;
  635: 			false ->
  636: 			    exit({not_equal,{Res1,Res2,A}})
  637: 		    end;
  638: 	    ({A,2,infix}) ->
  639: 		    L = atom_to_list(A),
  640: 		    Bin1 = list_to_binary(
  641: 			     [
  642: 			      <<"ets:fun2ms(fun({X,Y}) when X ">>,
  643: 			      L,<<" Y -> X ">>,
  644: 			      L,<<" Y end)">>
  645: 			     ]),
  646: 		    Bin2 = list_to_binary(
  647: 			     [
  648: 			      <<"ets:fun2ms(fun({X,Y}) when erlang:'">>,
  649: 			      L,<<"'(X,Y) -> erlang:'">>,
  650: 			      L,<<"'(X,Y) end)">>
  651: 			     ]),
  652: 		    Res1 = compile_and_run(Bin1),
  653: 		    Res2 = compile_and_run(Bin2),
  654: 		    case Res1 =:= Res2 of
  655: 			true ->
  656: 			    ok;
  657: 			false ->
  658: 			    exit({not_equal,{Res1,Res2,A}})
  659: 		    end;
  660: 	    ({A,2,magic}) -> %is_record
  661: 		    L = atom_to_list(A),
  662: 		    Bin1 = list_to_binary(
  663: 			     [
  664: 			      <<"ets:fun2ms(fun(X) when ">>,
  665: 			      L,<<"(X,a) -> ">>,
  666: 			      L,<<"(X,a) end)">>
  667: 			     ]),
  668: 		    Bin2 = list_to_binary(
  669: 			     [
  670: 			      <<"ets:fun2ms(fun(X) when erlang:'">>,
  671: 			      L,<<"'(X,a) -> erlang:'">>,
  672: 			      L,<<"'(X,a) end)">>
  673: 			     ]),
  674: 		    Res1 = compile_and_run(RD,Bin1),
  675: 		    Res2 = compile_and_run(RD,Bin2),
  676: 		    case Res1 =:= Res2 of
  677: 			true ->
  678: 			    ok;
  679: 			false ->
  680: 			    exit({not_equal,{Res1,Res2,A}})
  681: 		    end
  682: 	    end,
  683: 	    Allowed),
  684:     ok.
  685: 
  686: semicolon(suite) ->
  687:     [];
  688: semicolon(doc) ->
  689:     ["Tests semicolon in guards of match_specs."];
  690: semicolon(Config) when is_list(Config) ->
  691:     ?line setup(Config),
  692:     ?line Res01 = compile_and_run
  693: 		   (<<"ets:fun2ms(fun(X) when is_integer(X); "
  694: 		     "is_float(X) -> true end)">>),
  695:     ?line Res02 = compile_and_run
  696: 		   (<<"ets:fun2ms(fun(X) when is_integer(X) -> true; "
  697: 		     "(X) when is_float(X) -> true end)">>),
  698:     ?line Res01 = Res02,
  699:     ?line Res11 = compile_and_run
  700: 		   (<<"ets:fun2ms(fun(X) when is_integer(X); "
  701: 		     "is_float(X); atom(X) -> true end)">>),
  702:     ?line Res12 = compile_and_run
  703: 		   (<<"ets:fun2ms(fun(X) when is_integer(X) -> true; "
  704: 		     "(X) when is_float(X) -> true; "
  705: 		     "(X) when is_atom(X) -> true end)">>),
  706:     ?line Res11 = Res12,
  707:     ok.
  708:     
  709:     
  710: float_1_function(suite) ->
  711:     [];
  712: float_1_function(doc) ->
  713:     ["OTP-5297. The function float/1."];
  714: float_1_function(Config) when is_list(Config) ->
  715:     ?line setup(Config),
  716:     RunMS = fun(L, MS) -> 
  717:                     ets:match_spec_run(L, ets:match_spec_compile(MS)) 
  718:             end,
  719:     ?line MS1 = compile_and_run
  720:                   (<<"ets:fun2ms(fun(X) -> float(X) end)">>),
  721:     ?line [F1] = RunMS([3], MS1),
  722:     ?line true = is_float(F1) and (F1 == 3),
  723:                   
  724:     ?line MS1b = compile_and_run
  725:                   (<<"dbg:fun2ms(fun(X) -> float(X) end)">>),
  726:     ?line [F2] = RunMS([3], MS1b),
  727:     ?line true = is_float(F2) and (F2 == 3),
  728:                   
  729:     ?line MS2 = compile_and_run
  730:             (<<"ets:fun2ms(fun(X) when is_pid(X) or float(X) -> true end)">>),
  731:     ?line [] = RunMS([3.0], MS2),
  732: 
  733:     ?line MS3 = compile_and_run
  734:             (<<"dbg:fun2ms(fun(X) when is_pid(X); float(X) -> true end)">>),
  735:     ?line [true] = RunMS([3.0], MS3),
  736: 
  737:     ?line MS4 = compile_and_run
  738:             (<<"ets:fun2ms(fun(X) when erlang:float(X) > 1 -> big;"
  739:                "              (_) -> small end)">>),
  740:     ?line [small,big] = RunMS([1.0, 3.0], MS4),
  741: 
  742:     ?line MS5 = compile_and_run
  743:             (<<"ets:fun2ms(fun(X) when float(X) > 1 -> big;"
  744:                "              (_) -> small end)">>),
  745:     ?line [small,big] = RunMS([1.0, 3.0], MS5),
  746: 
  747:     %% This is the test from autoimported/1.
  748:     ?line [{'$1',[{is_float,'$1'}],[{float,'$1'}]}] = 
  749:         compile_and_run
  750:             (<<"ets:fun2ms(fun(X) when float(X) -> float(X) end)">>),
  751:     ?line [{'$1',[{float,'$1'}],[{float,'$1'}]}] =
  752:         compile_and_run
  753:            (<<"ets:fun2ms(fun(X) when erlang:'float'(X) -> "
  754:               "erlang:'float'(X) end)">>),
  755:     ok.
  756: 
  757: 
  758: action_function(suite) ->
  759:     [];
  760: action_function(doc) ->
  761:     ["Test all 'action functions'."];
  762: action_function(Config) when is_list(Config) ->
  763:     ?line setup(Config),
  764:     ?line [{['$1','$2'],[],
  765: 	    [{set_seq_token,label,0},
  766: 	     {get_seq_token},
  767: 	     {message,'$1'},
  768: 	     {return_trace},
  769: 	     {exception_trace}]}] =
  770: 	compile_and_run
  771: 	  (<<"dbg:fun2ms(fun([X,Y]) -> "
  772: 	    "set_seq_token(label, 0), "
  773: 	    "get_seq_token(), "
  774: 	    "message(X), "
  775: 	    "return_trace(), "
  776: 	    "exception_trace() end)">>),
  777:     ?line [{['$1','$2'],[],
  778: 	    [{process_dump},
  779: 	     {enable_trace,send},
  780: 	     {enable_trace,'$2',send},
  781: 	     {disable_trace,procs},
  782: 	     {disable_trace,'$2',procs}]}] =
  783: 	compile_and_run
  784: 	  (<<"dbg:fun2ms(fun([X,Y]) -> "
  785: 	    "process_dump(), "
  786: 	    "enable_trace(send), "
  787: 	    "enable_trace(Y, send), "
  788: 	    "disable_trace(procs), "
  789: 	    "disable_trace(Y, procs) end)">>),
  790:     ?line [{['$1','$2'],
  791: 	    [],
  792: 	    [{display,'$1'},
  793: 	     {caller},
  794: 	     {set_tcw,{const,16}},
  795: 	     {silent,true},
  796: 	     {trace,[send],[procs]},
  797: 	     {trace,'$2',[procs],[send]}]}] =
  798: 	compile_and_run
  799: 	  (<<"A = 16, dbg:fun2ms(fun([X,Y]) -> "
  800: 	    "display(X), "
  801: 	    "caller(), "
  802: 	    "set_tcw(A), "
  803: 	    "silent(true), "
  804: 	    "trace([send], [procs]), "
  805: 	    "trace(Y, [procs], [send])  end)">>),
  806:     ok.
  807: 
  808: 
  809: 
  810: 
  811: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  812: % Helpers
  813: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  814: 
  815: setup(Config) ->
  816:     put(mts_config,Config),
  817:     put(mts_tf_counter,0).
  818: 
  819: temp_name() ->
  820:     Conf = get(mts_config),
  821:     C = get(mts_tf_counter),
  822:     put(mts_tf_counter,C+1),
  823:     filename:join([?config(priv_dir,Conf),
  824: 		   "tempfile"++integer_to_list(C)++".tmp"]).
  825: 
  826: 
  827: expect_failure(Recs,Code) ->
  828:     case (catch compile_and_run(Recs,Code)) of
  829: 	      {'EXIT',_Foo} ->
  830: 		  %erlang:display(_Foo),
  831: 		  ok;
  832: 	      Other ->
  833: 		  exit({expected,failure,got,Other})
  834: 	  end.
  835:  
  836: compile_and_run(Expr) ->
  837:     compile_and_run(<<>>,Expr).
  838: compile_and_run(Records,Expr) ->
  839:     Prog = <<
  840: 	"-module(tmp).\n",
  841:     "-include_lib(\"stdlib/include/ms_transform.hrl\").\n",
  842:     "-export([tmp/0]).\n",
  843:     Records/binary,"\n",
  844:     "tmp() ->\n",
  845:     Expr/binary,".\n">>,
  846:     FN=temp_name(),
  847:     file:write_file(FN,Prog),
  848:     {ok,Forms} = epp:parse_file(FN,"",""),
  849:     {ok,tmp,Bin} = compile:forms(Forms),
  850:     code:load_binary(tmp,FN,Bin),
  851:     tmp:tmp().
  852: 
  853: compile_ww(Expr) ->
  854:     compile_ww(<<>>,Expr).
  855: compile_ww(Records,Expr) ->
  856:     Prog = <<
  857: 	"-module(tmp).\n",
  858:     "-include_lib(\"stdlib/include/ms_transform.hrl\").\n",
  859:     "-export([tmp/0]).\n",
  860:     Records/binary,"\n",
  861:     "tmp() ->\n",
  862:     Expr/binary,".\n">>,
  863:     FN=temp_name(),
  864:     file:write_file(FN,Prog),
  865:     {ok,Forms} = epp:parse_file(FN,"",""),
  866:     {ok,tmp,_Bin,Wlist} = compile:forms(Forms,[return_warnings,
  867: 					       nowarn_unused_vars,
  868: 					       nowarn_unused_record]),
  869:     Wlist.
  870: 
  871: compile_no_ww(Expr) ->
  872:     Prog = <<
  873: 	"-module(tmp).\n",
  874:     "-include_lib(\"stdlib/include/ms_transform.hrl\").\n",
  875:     "-export([tmp/1]).\n\n",
  876:     Expr/binary,".\n">>,
  877:     FN=temp_name(),
  878:     file:write_file(FN,Prog),
  879:     {ok,Forms} = epp:parse_file(FN,"",""),
  880:     {ok,tmp,_Bin,Wlist} = compile:forms(Forms,[return_warnings,
  881: 					       nowarn_unused_vars,
  882: 					       nowarn_unused_record]),
  883:     Wlist.
  884: 
  885: do_eval(String) ->
  886:     {done,{ok,T,_},[]} = erl_scan:tokens(
  887: 			   [],
  888: 			   String++".\n",1),
  889:     {ok,Tree} = erl_parse:parse_exprs(T),
  890:     {value,Res,[]} =  erl_eval:exprs(Tree,[]),
  891:     Res.