1: %%
    2: %% %CopyrightBegin%
    3: %% 
    4: %% Copyright Ericsson AB 1997-2013. 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(tuple_SUITE).
   20: -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, 
   21: 	 init_per_group/2,end_per_group/2, 
   22: 	 t_size/1, t_tuple_size/1, t_element/1, t_setelement/1,
   23: 	 t_insert_element/1, t_delete_element/1,
   24: 	 t_list_to_tuple/1, t_tuple_to_list/1,
   25: 	 t_make_tuple_2/1, t_make_tuple_3/1, t_append_element/1,
   26: 	 build_and_match/1, tuple_with_case/1, tuple_in_guard/1]).
   27: -include_lib("test_server/include/test_server.hrl").
   28: 
   29: %% Tests tuples and the BIFs:
   30: %%
   31: %% size(Tuple)
   32: %% element/2
   33: %% setelement/3
   34: %% tuple_to_list/1
   35: %% list_to_tuple/1
   36: %% make_tuple/2
   37: %%
   38: 
   39: suite() -> [{ct_hooks,[ts_install_cth]}].
   40: 
   41: all() -> 
   42:     [build_and_match, t_size, t_tuple_size, t_list_to_tuple,
   43:      t_tuple_to_list, t_element, t_setelement,
   44:      t_make_tuple_2, t_make_tuple_3, t_append_element,
   45:      t_insert_element, t_delete_element,
   46:      tuple_with_case, tuple_in_guard].
   47: 
   48: groups() -> 
   49:     [].
   50: 
   51: init_per_suite(Config) ->
   52:     Config.
   53: 
   54: end_per_suite(_Config) ->
   55:     ok.
   56: 
   57: init_per_group(_GroupName, Config) ->
   58:     Config.
   59: 
   60: end_per_group(_GroupName, Config) ->
   61:     Config.
   62: 
   63: 
   64: build_and_match(Config) when is_list(Config) ->
   65:     {} = id({}),
   66:     {1} = id({1}),
   67:     {1, 2} = id({1, 2}),
   68:     {1, 2, 3} = id({1, 2, 3}),
   69:     {1, 2, 3, 4} = id({1, 2, 3, 4}),
   70:     {1, 2, 3, 4, 5} = id({1, 2, 3, 4, 5}),
   71:     {1, 2, 3, 4, 5, 6} = id({1, 2, 3, 4, 5, 6}),
   72:     {1, 2, 3, 4, 5, 6} = id({1, 2, 3, 4, 5, 6}),
   73:     {1, 2, 3, 4, 5, 6, 7} = id({1, 2, 3, 4, 5, 6, 7}),
   74:     {1, 2, 3, 4, 5, 6, 7, 8} = id({1, 2, 3, 4, 5, 6, 7, 8}),
   75:     ok.
   76: 
   77: %% Tests size(Tuple).
   78: 
   79: t_size(Config) when is_list(Config) ->
   80:     0 = size({}),
   81:     1 = size({a}),
   82:     1 = size({{a}}),
   83:     2 = size({{a}, {b}}),
   84:     3 = size({1, 2, 3}),
   85:     ok.
   86: 
   87: t_tuple_size(Config) when is_list(Config) ->
   88:     0 = tuple_size(id({})),
   89:     1 = tuple_size(id({a})),
   90:     1 = tuple_size(id({{a}})),
   91:     2 = tuple_size(id({{a},{b}})),
   92:     3 = tuple_size(id({1,2,3})),
   93: 
   94:     %% Error cases.
   95:     {'EXIT',{badarg,_}} = (catch tuple_size([])),
   96:     {'EXIT',{badarg,_}} = (catch tuple_size(<<1,2,3>>)),
   97:     error = ludicrous_tuple_size({a,b,c}),
   98:     error = ludicrous_tuple_size([a,b,c]),
   99:     ok.
  100: 
  101: 
  102: ludicrous_tuple_size(T)
  103:   when tuple_size(T) =:= 16#7777777777777777777777777777777777 -> ok;
  104: ludicrous_tuple_size(_) -> error.
  105: 
  106: %% Tests element/2.
  107: 
  108: t_element(Config) when is_list(Config) ->
  109:     a = element(1, {a}),
  110:     a = element(1, {a, b}),
  111: 
  112:     List = lists:seq(1, 4096),
  113:     Tuple = list_to_tuple(lists:seq(1, 4096)),
  114:     get_elements(List, Tuple, 1),
  115: 
  116:     {'EXIT', {badarg, _}} = (catch element(0, id({a,b}))),
  117:     {'EXIT', {badarg, _}} = (catch element(3, id({a,b}))),
  118:     {'EXIT', {badarg, _}} = (catch element(1, id({}))),
  119:     {'EXIT', {badarg, _}} = (catch element(1, id([a,b]))),
  120:     {'EXIT', {badarg, _}} = (catch element(1, id(42))),
  121:     {'EXIT', {badarg, _}} = (catch element(id(1.5), id({a,b}))),
  122: 
  123:     ok.
  124: 
  125: get_elements([Element|Rest], Tuple, Pos) ->
  126:     Element = element(Pos, Tuple),
  127:     get_elements(Rest, Tuple, Pos+1);
  128: get_elements([], _Tuple, _Pos) ->
  129:     ok.
  130:     
  131: %% Tests set_element/3.
  132: 
  133: t_setelement(Config) when is_list(Config) ->
  134:     {x} = setelement(1, id({1}), x),
  135:     {x,2} = setelement(1, id({1,2}), x),
  136:     {1,x} = setelement(2, id({1,2}), x),
  137: 
  138:     Tuple = list_to_tuple(lists:duplicate(2048, x)),
  139:     NewTuple = set_all_elements(Tuple, 1),
  140:     NewTuple = list_to_tuple(lists:seq(1+7, 2048+7)),
  141: 
  142:     {'EXIT', {badarg, _}} = (catch setelement(0, {a, b}, x)),
  143:     {'EXIT', {badarg, _}} = (catch setelement(3, {a, b}, x)),
  144:     {'EXIT', {badarg, _}} = (catch setelement(1, {}, x)),
  145:     {'EXIT', {badarg, _}} = (catch setelement(1, [a, b], x)),
  146:     {'EXIT', {badarg, _}} = (catch setelement(1.5, {a, b}, x)),
  147: 
  148:     %% Nested setelement with literals.
  149:     AnotherTuple = id({0,0,a,b,c}),
  150:     {93748793749387837476555412,3.0,gurka,b,c} =
  151: 	setelement(1, setelement(2, setelement(3, AnotherTuple, gurka),
  152: 				 3.0), 93748793749387837476555412),
  153: 
  154:     ok.
  155: 
  156: set_all_elements(Tuple, Pos) when Pos =< size(Tuple) ->
  157:     set_all_elements(setelement(Pos, Tuple, Pos+7), Pos+1);
  158: set_all_elements(Tuple, Pos) when Pos > size(Tuple) ->
  159:     Tuple.
  160: 
  161: %% Tests list_to_tuple/1.
  162: 
  163: t_list_to_tuple(Config) when is_list(Config) ->
  164:     {} = list_to_tuple([]),
  165:     {a} = list_to_tuple([a]),
  166:     {a, b} = list_to_tuple([a, b]),
  167:     {a, b, c} = list_to_tuple([a, b, c]),
  168:     {a, b, c, d} = list_to_tuple([a, b, c, d]),
  169:     {a, b, c, d, e} = list_to_tuple([a, b, c, d, e]),
  170: 
  171:     Size  = 4096,
  172:     Tuple = list_to_tuple(lists:seq(1, Size)),
  173:     Size  = size(Tuple),
  174: 
  175:     {'EXIT', {badarg, _}} = (catch list_to_tuple(id({a,b}))),
  176:     {'EXIT', {badarg, _}} = (catch list_to_tuple(id([a|b]))),
  177:     {'EXIT', {badarg, _}} = (catch list_to_tuple(id([a|b]))),
  178: 
  179:     % test upper boundry, 16777215 elements
  180:     MaxSize  = 1 bsl 24 - 1,
  181:     MaxTuple = list_to_tuple(lists:seq(1, MaxSize)),
  182:     MaxSize  = size(MaxTuple),
  183: 
  184:     {'EXIT', {badarg,_}} = (catch list_to_tuple(lists:seq(1, 1 bsl 24))),
  185:     ok.
  186: 
  187: %% Tests tuple_to_list/1.
  188: 
  189: t_tuple_to_list(Config) when is_list(Config) ->
  190:     [] = tuple_to_list({}),
  191:     [a] = tuple_to_list({a}),
  192:     [a, b] = tuple_to_list({a, b}),
  193:     [a, b, c] = tuple_to_list({a, b, c}),
  194:     [a, b, c, d] = tuple_to_list({a, b, c, d}),
  195:     [a, b, c, d] = tuple_to_list({a, b, c, d}),
  196: 
  197:     Size = 4096,
  198:     List = lists:seq(1, Size),
  199:     Tuple = list_to_tuple(List),
  200:     Size = size(Tuple),
  201:     List = tuple_to_list(Tuple),
  202: 
  203:     {'EXIT', {badarg,_}} = (catch tuple_to_list(id(a))),
  204:     {'EXIT', {badarg,_}} = (catch tuple_to_list(id(42))),
  205: 
  206:     ok.
  207: 
  208: %% Tests the make_tuple/2 BIF.
  209: t_make_tuple_2(Config) when is_list(Config) ->
  210:     t_make_tuple1([]),
  211:     t_make_tuple1(42),
  212:     t_make_tuple1(a),
  213:     t_make_tuple1({}),
  214:     t_make_tuple1({a}),
  215:     t_make_tuple1(erlang:make_tuple(400, [])),
  216: 
  217:     % test upper boundry, 16777215 elements
  218:     t_make_tuple(1 bsl 24 - 1, a),
  219:     {'EXIT', {badarg,_}} = (catch erlang:make_tuple(1 bsl 24, a)),
  220: 
  221:     {'EXIT', {badarg,_}} = (catch erlang:make_tuple(-1, a)),
  222:     % 26 bits is the total header arity room (for now)
  223:     {'EXIT', {badarg,_}} = (catch erlang:make_tuple(1 bsl 26 + 3, a)),
  224:     % bignum
  225:     {'EXIT', {badarg,_}} = (catch erlang:make_tuple(1 bsl 65 + 3, a)),
  226:     ok.
  227: 
  228: t_make_tuple1(Element) ->
  229:     lists:foreach(fun(Size) -> t_make_tuple(Size, Element) end,
  230: 		  [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 255, 256, 511, 512, 999,
  231: 		   1000, 1023, 1024, 4095, 4096]).
  232: 
  233: t_make_tuple(Size, Element) ->
  234:     Tuple = erlang:make_tuple(Size, Element),
  235:     lists:foreach(fun(El) when El =:= Element ->
  236: 			  ok;
  237: 		     (Other) ->
  238: 			  test_server:fail({got, Other, expected, Element})
  239: 		  end, tuple_to_list(Tuple)).
  240: 
  241: %% Tests the erlang:make_tuple/3 BIF.
  242: t_make_tuple_3(Config) when is_list(Config) ->
  243:     {}    = erlang:make_tuple(0, def, []),
  244:     {def} = erlang:make_tuple(1, def, []),
  245:     {a}   = erlang:make_tuple(1, def, [{1,a}]),
  246: 
  247:     {a,def,c,def,e} = erlang:make_tuple(5, def, [{5,e},{1,a},{3,c}]),
  248:     {a,def,c,def,e} = erlang:make_tuple(5, def, [{1,blurf},{5,e},{3,blurf},{1,a},{3,c}]),
  249:     MaxSize  = 1 bsl 16 - 1,
  250:     MaxTuple = erlang:make_tuple(MaxSize, def, [{1,blurf},{5,e},{3,blurf},{1,a},{3,c}]),
  251:     MaxSize  = size(MaxTuple),
  252: 
  253:     %% Error cases.
  254:     {'EXIT',{badarg,_}} = (catch erlang:make_tuple(0, def, [{1,a}])),
  255:     {'EXIT',{badarg,_}} = (catch erlang:make_tuple(5, def, [{-1,a}])),
  256:     {'EXIT',{badarg,_}} = (catch erlang:make_tuple(5, def, [{0,a}])),
  257:     {'EXIT',{badarg,_}} = (catch erlang:make_tuple(5, def, [{6,z}])),
  258:     {'EXIT',{badarg,_}} = (catch erlang:make_tuple(a, def, [{6,z}])),
  259:     {'EXIT',{badarg,_}} = (catch erlang:make_tuple(5, def, [{1,a}|b])),
  260:     {'EXIT',{badarg,_}} = (catch erlang:make_tuple(5, def, [42])),
  261:     {'EXIT',{badarg,_}} = (catch erlang:make_tuple(5, def, [[a,b,c]])),
  262:     {'EXIT',{badarg,_}} = (catch erlang:make_tuple(5, def, non_list)),
  263:     {'EXIT',{badarg,_}} = (catch erlang:make_tuple(1 bsl 24, def, [{5,e},{1,a},{3,c}])),
  264: 
  265:     ok.
  266: 
  267: %% Tests the erlang:insert_element/3 BIF.
  268: t_insert_element(Config) when is_list(Config) ->
  269:     {a}       = erlang:insert_element(1, {}, a),
  270:     {{b,b},a} = erlang:insert_element(1, {a}, {b,b}),
  271:     {a,b}     = erlang:insert_element(2, {a}, b),
  272:     [b,def|_] = tuple_to_list(erlang:insert_element(1, erlang:make_tuple(1 bsl 20, def), b)),
  273:     [def,b|_] = tuple_to_list(erlang:insert_element(2, erlang:make_tuple(1 bsl 20, def), b)),
  274:     [def,b|_] = lists:reverse(tuple_to_list(erlang:insert_element(1 bsl 20, erlang:make_tuple(1 bsl 20, def), b))),
  275:     [b,def|_] = lists:reverse(tuple_to_list(erlang:insert_element((1 bsl 20) + 1, erlang:make_tuple(1 bsl 20, def), b))),
  276: 
  277:     %% Error cases.
  278:     {'EXIT',{badarg,_}} = (catch erlang:insert_element(1, [], a)),
  279:     {'EXIT',{badarg,_}} = (catch erlang:insert_element(1, a, a)),
  280:     {'EXIT',{badarg,_}} = (catch erlang:insert_element(0, {}, a)),
  281:     {'EXIT',{badarg,_}} = (catch erlang:insert_element(0, {b,b,b,b,b}, a)),
  282:     {'EXIT',{badarg,_}} = (catch erlang:insert_element(-1, {}, a)),
  283:     {'EXIT',{badarg,_}} = (catch erlang:insert_element(2, {}, a)),
  284:     {'EXIT',{badarg,_}} = (catch erlang:insert_element(6, {b,b,b,b}, a)),
  285:     {'EXIT',{badarg,_}} = (catch erlang:insert_element(1 bsl 20, {b,b,b,b}, a)),
  286:     ok.
  287: 
  288: %% Tests the erlang:delete_element/3 BIF.
  289: t_delete_element(Config) when is_list(Config) ->
  290:     {}        = erlang:delete_element(1, {a}),
  291:     {{b,b},c} = erlang:delete_element(1, {a,{b,b},c}),
  292:     {a,b}     = erlang:delete_element(2, {a,c,b}),
  293:     [2,3|_]   = tuple_to_list(erlang:delete_element(1, list_to_tuple(lists:seq(1, 1 bsl 20)))),
  294:     [1,3|_]   = tuple_to_list(erlang:delete_element(2, list_to_tuple(lists:seq(1, 1 bsl 20)))),
  295:     [(1 bsl 20) - 1, (1 bsl 20) - 2 |_] = lists:reverse(tuple_to_list(erlang:delete_element(1 bsl 20, list_to_tuple(lists:seq(1, 1 bsl 20))))),
  296:     [(1 bsl 20), (1 bsl 20) - 2 |_] = lists:reverse(tuple_to_list(erlang:delete_element((1 bsl 20) - 1, list_to_tuple(lists:seq(1, 1 bsl 20))))),
  297: 
  298:     %% Error cases.
  299:     {'EXIT',{badarg,_}} = (catch erlang:delete_element(1, [])),
  300:     {'EXIT',{badarg,_}} = (catch erlang:delete_element(1, a)),
  301:     {'EXIT',{badarg,_}} = (catch erlang:delete_element(0, {})),
  302:     {'EXIT',{badarg,_}} = (catch erlang:delete_element(-1, {})),
  303:     {'EXIT',{badarg,_}} = (catch erlang:delete_element(1, {})),
  304:     {'EXIT',{badarg,_}} = (catch erlang:delete_element(0, {b,b,b,b,b})),
  305:     {'EXIT',{badarg,_}} = (catch erlang:delete_element(5, {b,b,b,b})),
  306:     {'EXIT',{badarg,_}} = (catch erlang:delete_element(1 bsl 20, {b,c,b,b,b})),
  307:     ok.
  308: 
  309: 
  310: %% Tests the append_element/2 BIF.
  311: t_append_element(Config) when is_list(Config) ->
  312:     ok = t_append_element({}, 2048, 2048),
  313: 
  314:     % test upper boundry, 16777215 elements
  315:     MaxSize  = 1 bsl 24 - 1,
  316:     MaxTuple = list_to_tuple(lists:seq(1, MaxSize)),
  317:     {'EXIT',{badarg,_}} = (catch erlang:append_element(MaxTuple, a)),
  318:     ok.
  319: 
  320: t_append_element(_Tuple, 0, _High) -> ok;
  321: t_append_element(Tuple, N, High) ->
  322:     NewTuple = erlang:append_element(Tuple, N),
  323:     verify_seq(tuple_to_list(Tuple), High, N),
  324:     t_append_element(NewTuple, N-1, High).
  325: 
  326: verify_seq([], High, High) -> ok;
  327: verify_seq([High], High, High) -> ok;
  328: verify_seq([High|T], High, Lower) ->
  329:     verify_seq(T, High-1, Lower).
  330: 
  331: %% Tests that a case nested inside a tuple is ok.
  332: %% (This is known to crash earlier versions of BEAM.)
  333: 
  334: tuple_with_case(Config) when is_list(Config) ->
  335:     {reply, true} = tuple_with_case(),
  336:     ok.
  337: 
  338: tuple_with_case() ->
  339:     %% The following comments apply to the BEAM compiler.
  340:     foo(),                              % Reset var count.
  341:     {reply,                             % Compiler will choose {x,1} for tuple.
  342:      case foo() of                      % Call will reset var count.
  343:          {'EXIT', Reason} ->		% Case will return in {x,1} (first free).
  344:              {error, Reason};           % but the tuple will be build in {x,1},
  345:          _ ->                           % so case value is lost and a circular
  346:              true                       % data element is built.
  347:      end}.
  348: 
  349: foo() -> ignored.
  350: 
  351: %% Test to build a tuple in a guard.
  352: 
  353: tuple_in_guard(Config) when is_list(Config) ->
  354:     Tuple1 = id({a,b}),
  355:     Tuple2 = id({a,b,c}),
  356:     if
  357: 	Tuple1 == {element(1, Tuple2),element(2, Tuple2)} ->
  358: 	    ok;
  359: 	true ->
  360: 	    test_server:fail()
  361:     end,
  362:     if
  363: 	Tuple2 == {element(1, Tuple2),element(2, Tuple2),
  364: 	    element(3, Tuple2)} ->
  365: 	    ok;
  366: 	true ->
  367: 	    test_server:fail()
  368:     end,
  369:     ok.
  370: 
  371: %% Use this function to avoid compile-time evaluation of an expression.
  372: id(I) -> I.
  373: