1: %%
    2: %% %CopyrightBegin%
    3: %%
    4: %% Copyright Ericsson AB 2004-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: %%% Purpose : Test records.
   22: 
   23: -module(record_SUITE).
   24: 
   25: -include_lib("test_server/include/test_server.hrl").
   26: 
   27: -export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2,
   28: 	 init_per_testcase/2,end_per_testcase/2,
   29: 	 init_per_suite/1,end_per_suite/1,
   30: 	 errors/1,record_test/1,eval_once/1]).
   31: 
   32: suite() -> [{ct_hooks,[ts_install_cth]}].
   33: 
   34: all() -> 
   35:     cases().
   36: 
   37: groups() -> 
   38:     [].
   39: 
   40: init_per_group(_GroupName, Config) ->
   41:     Config.
   42: 
   43: end_per_group(_GroupName, Config) ->
   44:     Config.
   45: 
   46: 
   47: cases() -> 
   48:     [errors, record_test, eval_once].
   49: 
   50: init_per_testcase(_Case, Config) ->
   51:     test_lib:interpret(?MODULE),
   52:     Dog = test_server:timetrap(?t:minutes(1)),
   53:     [{watchdog,Dog}|Config].
   54: 
   55: end_per_testcase(_Case, Config) ->
   56:     Dog = ?config(watchdog, Config),
   57:     ?t:timetrap_cancel(Dog),
   58:     ok.
   59: 
   60: init_per_suite(Config) when is_list(Config) ->
   61:     ?line test_lib:interpret(?MODULE),
   62:     ?line true = lists:member(?MODULE, int:interpreted()),
   63:     Config.
   64: 
   65: end_per_suite(Config) when is_list(Config) ->
   66:     ok.
   67: 
   68: -record(foo, {a,b,c,d}).
   69: -record(bar, {a,b,c,d}).
   70: -record(barf, {a,b,c,d,e}).
   71: 
   72: errors(Config) when is_list(Config) ->
   73:     Foo = #foo{a=1,b=2,c=3,d=4},
   74:     ?line #foo{a=19,b=42,c=3,d=4} = update_foo(Foo, 19, 42),
   75: 
   76:     ?line {'EXIT',{{badrecord,bar},_}} = (catch update_foo_bar(Foo, 19)),
   77:     ?line {'EXIT',{{badrecord,bar},_}} = (catch update_foo_bar(Foo, 19, 35)),
   78:     ?line {'EXIT',{{badrecord,bar},_}} = (catch update_foo_bar(Foo, 19, 35, 17)),
   79:     ?line {'EXIT',{{badrecord,bar},_}} = (catch update_foo_bar(Foo, 19, 35, 17, 42)),
   80: 
   81:     ?line {'EXIT',{{badrecord,barf},_}} = (catch update_foo_barf(Foo, 19)),
   82:     ?line {'EXIT',{{badrecord,barf},_}} = (catch update_foo_barf(Foo, 19, 35)),
   83:     ?line {'EXIT',{{badrecord,barf},_}} = (catch update_foo_barf(Foo, 19, 35, 17)),
   84:     ?line {'EXIT',{{badrecord,barf},_}} = (catch update_foo_barf(Foo, 19, 35, 17, 42)),
   85:     ?line {'EXIT',{{badrecord,barf},_}} = (catch update_foo_barf(Foo, 19,
   86: 								 35, 17, 42, -2)),
   87: 
   88:     ok.
   89: 
   90: update_foo(#foo{}=R, A, B) ->
   91:     R#foo{a=A,b=B}.
   92: 
   93: update_foo_bar(#foo{}=R, A) ->
   94:     R#bar{a=A}.
   95: 
   96: update_foo_bar(#foo{}=R, A, _B) ->
   97:     R#bar{a=A,b=A}.
   98: 
   99: update_foo_bar(#foo{}=R, A, _B, C) ->
  100:     R#bar{a=A,b=A,c=C}.
  101: 
  102: update_foo_bar(#foo{}=R, A, _B, C, D) ->
  103:     R#bar{a=A,b=A,c=C,d=D}.
  104: 
  105: update_foo_barf(#foo{}=R, A) ->
  106:     R#barf{a=A}.
  107: 
  108: update_foo_barf(#foo{}=R, A, _B) ->
  109:     R#barf{a=A,b=A}.
  110: 
  111: update_foo_barf(#foo{}=R, A, _B, C) ->
  112:     R#barf{a=A,b=A,c=C}.
  113: 
  114: update_foo_barf(#foo{}=R, A, _B, C, D) ->
  115:     R#barf{a=A,b=A,c=C,d=D}.
  116: 
  117: update_foo_barf(#foo{}=R, A, _B, C, D, E) ->
  118:     R#barf{a=A,b=A,c=C,d=D,e=E}.
  119: 
  120: 
  121: -define(TrueGuard(Expr), if Expr -> ok; true -> ?t:fail() end).
  122: -define(FalseGuard(Expr), if Expr -> ?t:fail(); true -> ok end).
  123: 
  124: record_test(Config) when is_list(Config) ->
  125:     ?line true = is_record(#foo{}, foo),
  126:     ?line false = is_record(#foo{}, barf),
  127:     ?line false = is_record({foo}, foo),
  128: 
  129:     ?line true = erlang:is_record(#foo{}, foo),
  130:     ?line false = erlang:is_record(#foo{}, barf),
  131:     ?line false = erlang:is_record({foo}, foo),
  132: 
  133:     ?line false = is_record([], foo),
  134:     ?line false = is_record(Config, foo),
  135: 
  136:     ?line ?TrueGuard(is_record(#foo{}, foo)),
  137:     ?line ?FalseGuard(is_record(#foo{}, barf)),
  138:     ?line ?FalseGuard(is_record({foo}, foo)),
  139: 
  140:     ?line ?TrueGuard(erlang:is_record(#foo{}, foo)),
  141:     ?line ?FalseGuard(erlang:is_record(#foo{}, barf)),
  142:     ?line ?FalseGuard(erlang:is_record({foo}, foo)),
  143: 
  144:     ?line ?FalseGuard(is_record([], foo)),
  145:     ?line ?FalseGuard(is_record(Config, foo)),
  146: 
  147:     %% 'not is_record/2' to test guard optimization.
  148: 
  149:     ?line ?FalseGuard(not is_record(#foo{}, foo)),
  150:     ?line ?TrueGuard(not is_record(#foo{}, barf)),
  151:     ?line ?TrueGuard(not is_record({foo}, foo)),
  152: 
  153:     ?line ?FalseGuard(not erlang:is_record(#foo{}, foo)),
  154:     ?line ?TrueGuard(not erlang:is_record(#foo{}, barf)),
  155:     ?line ?TrueGuard(not erlang:is_record({foo}, foo)),
  156: 
  157:     Foo = id(#foo{}),
  158:     ?line ?FalseGuard(not erlang:is_record(Foo, foo)),
  159:     ?line ?TrueGuard(not erlang:is_record(Foo, barf)),
  160: 
  161:     ?line ?TrueGuard(not is_record(Config, foo)),
  162: 
  163:     ?line ?TrueGuard(not is_record(a, foo)),
  164:     ?line ?TrueGuard(not is_record([], foo)),
  165: 
  166:     %% Pass non-literal first argument.
  167: 
  168:     ?line true = is_record(id(#foo{}), foo),
  169:     ?line false = is_record(id(#foo{}), barf),
  170:     ?line false = is_record(id({foo}), foo),
  171: 
  172:     ?line true = erlang:is_record(id(#foo{}), foo),
  173:     ?line false = erlang:is_record(id(#foo{}), barf),
  174:     ?line false = erlang:is_record(id({foo}), foo),
  175: 
  176:     NoRec1 = id(blurf),
  177:     NoRec2 = id([]),
  178: 
  179:     ?line ?TrueGuard(not is_record(NoRec1, foo)),
  180:     ?line ?TrueGuard(not is_record(NoRec2, foo)),
  181: 
  182:     %% Force the use of guard bifs by using the 'xor' operation.
  183: 
  184:     False = id(false),
  185:     ?line ?TrueGuard(is_record(#foo{}, foo) xor False),
  186:     ?line ?FalseGuard(is_record(#foo{}, barf) xor False),
  187:     ?line ?FalseGuard(is_record({foo}, foo) xor False ),
  188: 
  189:     ?line ?TrueGuard(is_record(Foo, foo) xor False),
  190:     ?line ?FalseGuard(is_record(Foo, barf) xor False),
  191: 
  192: 
  193:     %% Implicit guards by using a list comprehension.
  194: 
  195:     List = id([1,#foo{a=2},3,#bar{d=4},5,#foo{a=6},7]),
  196: 
  197:     ?line [#foo{a=2},#foo{a=6}] = [X || X <- List, is_record(X, foo)],
  198:     ?line [#bar{d=4}] = [X || X <- List, is_record(X, bar)],
  199:     ?line [1,#foo{a=2},3,5,#foo{a=6},7] =
  200: 	[X || X <- List, not is_record(X, bar)],
  201:     ?line [1,3,5,7] =
  202: 	[X || X <- List, ((not is_record(X, bar)) and (not is_record(X, foo)))],
  203:     ?line [#foo{a=2},#bar{d=4},#foo{a=6}] =
  204: 	[X || X <- List, ((is_record(X, bar)) or (is_record(X, foo)))],
  205:     ?line [1,3,#bar{d=4}] =
  206: 	[X || X <- List, ((is_record(X, bar)) or (X < 5))],
  207: 
  208:     ?line MyList = [#foo{a=3},x,[],{a,b}],
  209:     ?line [#foo{a=3}] = [X || X <- MyList, is_record(X, foo)],
  210:     ?line [x,[],{a,b}] = [X || X <- MyList, not is_record(X, foo)],
  211:     ?line [#foo{a=3}] = [X || X <- MyList, begin is_record(X, foo) end],
  212:     ?line [x,[],{a,b}] = [X || X <- MyList, begin not is_record(X, foo) end],
  213:     ?line [#foo{a=3},x,[],{a,b}] = [X || X <- MyList, is_record(X, foo) or
  214: 					     not is_binary(X)],
  215:     ?line [#foo{a=3},x,[],{a,b}] = [X || X <- MyList, not is_record(X, foo) or
  216: 					     not is_binary(X)],
  217:     ?line [#foo{a=3}] = [X || X <- MyList, is_record(X, foo) or is_reference(X)],
  218:     ?line [x,[],{a,b}] = [X || X <- MyList, not is_record(X, foo) or
  219: 				   is_reference(X)],
  220:     ?line [#foo{a=3},x,[],{a,b}] = [X || X <- MyList,
  221: 					 begin is_record(X, foo) or
  222: 						   not is_binary(X) end],
  223:     ?line [#foo{a=3},x,[],{a,b}] = [X || X <- MyList,
  224: 					 begin not is_record(X, foo) or
  225: 						   not is_binary(X) end],
  226:     ?line [#foo{a=3}] = [X || X <- MyList,
  227: 			      begin is_record(X, foo) or is_reference(X) end],
  228:     ?line [x,[],{a,b}] = [X || X <- MyList,
  229: 			       begin not is_record(X, foo) or
  230: 					 is_reference(X) end],
  231:     ok.
  232: 
  233: eval_once(Config) when is_list(Config) ->
  234:     ?line once(fun(GetRec) ->
  235: 		       true = erlang:is_record(GetRec(), foo)
  236: 	       end, #foo{}),
  237:     ?line once(fun(GetRec) ->
  238: 		       (GetRec())#foo{a=1}
  239: 	       end, #foo{}),
  240:     ?line once(fun(GetRec) ->
  241: 		       (GetRec())#foo{a=1,b=2}
  242: 	       end, #foo{}),
  243:     ?line once(fun(GetRec) ->
  244: 		       (GetRec())#foo{a=1,b=2,c=3}
  245: 	       end, #foo{}),
  246:     ?line once(fun(GetRec) ->
  247: 		       (GetRec())#foo{a=1,b=2,c=3,d=4}
  248: 	       end, #foo{}),
  249:     ok.
  250: 
  251: once(Test, Record) ->
  252:     put(?MODULE, 0),
  253:     GetRec = fun() ->
  254: 		     put(?MODULE, 1+get(?MODULE)),
  255: 		     Record
  256: 	     end,
  257:     Result = Test(GetRec),
  258:     case get(?MODULE) of
  259: 	1 -> ok;
  260: 	N ->
  261: 	    io:format("Evaluated ~w times\n", [N]),
  262: 	    ?t:fail()
  263:     end,
  264:     Result.
  265: 
  266: id(I) -> I.