1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 1997-2018. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20-module(tuple_SUITE).
21-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
22	 init_per_group/2,end_per_group/2,
23	 t_size/1, t_tuple_size/1, t_element/1, t_setelement/1,
24	 t_insert_element/1, t_delete_element/1,
25	 t_list_to_tuple/1, t_list_to_upper_boundry_tuple/1, t_tuple_to_list/1,
26	 t_make_tuple_2/1, t_make_upper_boundry_tuple_2/1, t_make_tuple_3/1,
27	 t_append_element/1, t_append_element_upper_boundry/1,
28	 build_and_match/1, tuple_with_case/1, tuple_in_guard/1]).
29-include_lib("common_test/include/ct.hrl").
30
31%% Tests tuples and the BIFs:
32%%
33%% size(Tuple)
34%% element/2
35%% setelement/3
36%% tuple_to_list/1
37%% list_to_tuple/1
38%% make_tuple/2
39%%
40
41suite() -> [{ct_hooks,[ts_install_cth]}].
42
43all() ->
44    [build_and_match, t_size, t_tuple_size, t_list_to_tuple,
45     t_list_to_upper_boundry_tuple,
46     t_tuple_to_list, t_element, t_setelement,
47     t_make_tuple_2, t_make_upper_boundry_tuple_2, t_make_tuple_3,
48     t_append_element, t_append_element_upper_boundry,
49     t_insert_element, t_delete_element,
50     tuple_with_case, tuple_in_guard].
51
52groups() ->
53    [].
54
55init_per_suite(Config) ->
56    A0 = case application:start(sasl) of
57	     ok -> [sasl];
58	     _ -> []
59	 end,
60    A = case application:start(os_mon) of
61	     ok -> [os_mon|A0];
62	     _ -> A0
63	 end,
64    [{started_apps, A}|Config].
65
66end_per_suite(Config) ->
67    As = proplists:get_value(started_apps, Config),
68    lists:foreach(fun (A) -> application:stop(A) end, As),
69    Config.
70
71init_per_group(_GroupName, Config) ->
72    Config.
73
74end_per_group(_GroupName, Config) ->
75    Config.
76
77
78build_and_match(Config) when is_list(Config) ->
79    {} = id({}),
80    {1} = id({1}),
81    {1, 2} = id({1, 2}),
82    {1, 2, 3} = id({1, 2, 3}),
83    {1, 2, 3, 4} = id({1, 2, 3, 4}),
84    {1, 2, 3, 4, 5} = id({1, 2, 3, 4, 5}),
85    {1, 2, 3, 4, 5, 6} = id({1, 2, 3, 4, 5, 6}),
86    {1, 2, 3, 4, 5, 6} = id({1, 2, 3, 4, 5, 6}),
87    {1, 2, 3, 4, 5, 6, 7} = id({1, 2, 3, 4, 5, 6, 7}),
88    {1, 2, 3, 4, 5, 6, 7, 8} = id({1, 2, 3, 4, 5, 6, 7, 8}),
89    ok.
90
91%% Tests size(Tuple).
92
93t_size(Config) when is_list(Config) ->
94    0 = size({}),
95    1 = size({a}),
96    1 = size({{a}}),
97    2 = size({{a}, {b}}),
98    3 = size({1, 2, 3}),
99    ok.
100
101t_tuple_size(Config) when is_list(Config) ->
102    0 = tuple_size(id({})),
103    1 = tuple_size(id({a})),
104    1 = tuple_size(id({{a}})),
105    2 = tuple_size(id({{a},{b}})),
106    3 = tuple_size(id({1,2,3})),
107
108    %% Error cases.
109    {'EXIT',{badarg,_}} = (catch tuple_size([])),
110    {'EXIT',{badarg,_}} = (catch tuple_size(<<1,2,3>>)),
111    error = ludicrous_tuple_size({a,b,c}),
112    error = ludicrous_tuple_size([a,b,c]),
113    ok.
114
115
116ludicrous_tuple_size(T)
117  when tuple_size(T) =:= 16#7777777777777777777777777777777777 -> ok;
118ludicrous_tuple_size(_) -> error.
119
120%% Tests element/2.
121
122t_element(Config) when is_list(Config) ->
123    a = element(1, {a}),
124    a = element(1, {a, b}),
125
126    List = lists:seq(1, 4096),
127    Tuple = list_to_tuple(lists:seq(1, 4096)),
128    get_elements(List, Tuple, 1),
129
130    {'EXIT', {badarg, _}} = (catch element(0, id({a,b}))),
131    {'EXIT', {badarg, _}} = (catch element(3, id({a,b}))),
132    {'EXIT', {badarg, _}} = (catch element(1, id({}))),
133    {'EXIT', {badarg, _}} = (catch element(1, id([a,b]))),
134    {'EXIT', {badarg, _}} = (catch element(1, id(42))),
135    {'EXIT', {badarg, _}} = (catch element(id(1.5), id({a,b}))),
136
137    %% Make sure that the loader does not reject the module when
138    %% huge literal index values are used.
139    {'EXIT', {badarg, _}} = (catch element((1 bsl 24)-1, id({a,b,c}))),
140    {'EXIT', {badarg, _}} = (catch element(1 bsl 24, id({a,b,c}))),
141    {'EXIT', {badarg, _}} = (catch element(1 bsl 32, id({a,b,c}))),
142    {'EXIT', {badarg, _}} = (catch element(1 bsl 64, id({a,b,c}))),
143
144    ok.
145
146get_elements([Element|Rest], Tuple, Pos) ->
147    Element = element(Pos, Tuple),
148    get_elements(Rest, Tuple, Pos+1);
149get_elements([], _Tuple, _Pos) ->
150    ok.
151
152%% Tests set_element/3.
153
154t_setelement(Config) when is_list(Config) ->
155    {x} = setelement(1, id({1}), x),
156    {x,2} = setelement(1, id({1,2}), x),
157    {1,x} = setelement(2, id({1,2}), x),
158
159    Tuple = list_to_tuple(lists:duplicate(2048, x)),
160    NewTuple = set_all_elements(Tuple, 1),
161    NewTuple = list_to_tuple(lists:seq(1+7, 2048+7)),
162
163    {'EXIT', {badarg, _}} = (catch setelement(0, {a, b}, x)),
164    {'EXIT', {badarg, _}} = (catch setelement(3, {a, b}, x)),
165    {'EXIT', {badarg, _}} = (catch setelement(1, {}, x)),
166    {'EXIT', {badarg, _}} = (catch setelement(1, [a, b], x)),
167    {'EXIT', {badarg, _}} = (catch setelement(1.5, {a, b}, x)),
168
169    %% Nested setelement with literals.
170    AnotherTuple = id({0,0,a,b,c}),
171    {93748793749387837476555412,3.0,gurka,b,c} =
172	setelement(1, setelement(2, setelement(3, AnotherTuple, gurka),
173				 3.0), 93748793749387837476555412),
174
175    ok.
176
177set_all_elements(Tuple, Pos) when Pos =< size(Tuple) ->
178    set_all_elements(setelement(Pos, Tuple, Pos+7), Pos+1);
179set_all_elements(Tuple, Pos) when Pos > size(Tuple) ->
180    Tuple.
181
182%% Tests list_to_tuple/1.
183
184t_list_to_tuple(Config) when is_list(Config) ->
185    {} = list_to_tuple([]),
186    {a} = list_to_tuple([a]),
187    {a, b} = list_to_tuple([a, b]),
188    {a, b, c} = list_to_tuple([a, b, c]),
189    {a, b, c, d} = list_to_tuple([a, b, c, d]),
190    {a, b, c, d, e} = list_to_tuple([a, b, c, d, e]),
191
192    Size  = 4096,
193    Tuple = list_to_tuple(lists:seq(1, Size)),
194    Size  = size(Tuple),
195
196    {'EXIT', {badarg, _}} = (catch list_to_tuple(id({a,b}))),
197    {'EXIT', {badarg, _}} = (catch list_to_tuple(id([a|b]))),
198    {'EXIT', {badarg, _}} = (catch list_to_tuple(id([a|b]))),
199
200    {'EXIT', {badarg,_}} = (catch list_to_tuple(lists:seq(1, 1 bsl 24))),
201    ok.
202
203t_list_to_upper_boundry_tuple(Config) when is_list(Config) ->
204    sys_mem_cond_run(2048,
205		    fun () ->
206			    %% test upper boundry, 16777215 elements
207			    MaxSize  = 1 bsl 24 - 1,
208			    MaxTuple = list_to_tuple(lists:seq(1, MaxSize)),
209			    MaxSize  = size(MaxTuple),
210			    ok
211		    end).
212
213%% Tests tuple_to_list/1.
214
215t_tuple_to_list(Config) when is_list(Config) ->
216    [] = tuple_to_list({}),
217    [a] = tuple_to_list({a}),
218    [a, b] = tuple_to_list({a, b}),
219    [a, b, c] = tuple_to_list({a, b, c}),
220    [a, b, c, d] = tuple_to_list({a, b, c, d}),
221    [a, b, c, d] = tuple_to_list({a, b, c, d}),
222
223    Size = 4096,
224    List = lists:seq(1, Size),
225    Tuple = list_to_tuple(List),
226    Size = size(Tuple),
227    List = tuple_to_list(Tuple),
228
229    {'EXIT', {badarg,_}} = (catch tuple_to_list(id(a))),
230    {'EXIT', {badarg,_}} = (catch tuple_to_list(id(42))),
231
232    ok.
233
234%% Tests the make_tuple/2 BIF.
235t_make_tuple_2(Config) when is_list(Config) ->
236    t_make_tuple1([]),
237    t_make_tuple1(42),
238    t_make_tuple1(a),
239    t_make_tuple1({}),
240    t_make_tuple1({a}),
241    t_make_tuple1(erlang:make_tuple(400, [])),
242
243    {'EXIT', {badarg,_}} = (catch erlang:make_tuple(1 bsl 24, a)),
244
245    {'EXIT', {badarg,_}} = (catch erlang:make_tuple(-1, a)),
246    % 26 bits is the total header arity room (for now)
247    {'EXIT', {badarg,_}} = (catch erlang:make_tuple(1 bsl 26 + 3, a)),
248    % bignum
249    {'EXIT', {badarg,_}} = (catch erlang:make_tuple(1 bsl 65 + 3, a)),
250    ok.
251
252t_make_upper_boundry_tuple_2(Config) when is_list(Config) ->
253    sys_mem_cond_run(2048,
254		     fun () ->
255			     %% test upper boundry, 16777215 elements
256			     t_make_tuple(1 bsl 24 - 1, a)
257		     end).
258
259t_make_tuple1(Element) ->
260    lists:foreach(fun(Size) -> t_make_tuple(Size, Element) end,
261		  [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 255, 256, 511, 512, 999,
262		   1000, 1023, 1024, 4095, 4096]).
263
264t_make_tuple(Size, Element) ->
265    Tuple = erlang:make_tuple(Size, Element),
266    lists:foreach(fun(El) when El =:= Element ->
267			  ok;
268		     (Other) ->
269			  ct:fail({got, Other, expected, Element})
270		  end, tuple_to_list(Tuple)).
271
272%% Tests the erlang:make_tuple/3 BIF.
273t_make_tuple_3(Config) when is_list(Config) ->
274    {}    = erlang:make_tuple(0, def, []),
275    {def} = erlang:make_tuple(1, def, []),
276    {a}   = erlang:make_tuple(1, def, [{1,a}]),
277
278    {a,def,c,def,e} = erlang:make_tuple(5, def, [{5,e},{1,a},{3,c}]),
279    {a,def,c,def,e} = erlang:make_tuple(5, def, [{1,blurf},{5,e},{3,blurf},{1,a},{3,c}]),
280    MaxSize  = 1 bsl 16 - 1,
281    MaxTuple = erlang:make_tuple(MaxSize, def, [{1,blurf},{5,e},{3,blurf},{1,a},{3,c}]),
282    MaxSize  = size(MaxTuple),
283
284    %% Error cases.
285    {'EXIT',{badarg,_}} = (catch erlang:make_tuple(0, def, [{1,a}])),
286    {'EXIT',{badarg,_}} = (catch erlang:make_tuple(5, def, [{-1,a}])),
287    {'EXIT',{badarg,_}} = (catch erlang:make_tuple(5, def, [{0,a}])),
288    {'EXIT',{badarg,_}} = (catch erlang:make_tuple(5, def, [{6,z}])),
289    {'EXIT',{badarg,_}} = (catch erlang:make_tuple(a, def, [{6,z}])),
290    {'EXIT',{badarg,_}} = (catch erlang:make_tuple(5, def, [{1,a}|b])),
291    {'EXIT',{badarg,_}} = (catch erlang:make_tuple(5, def, [42])),
292    {'EXIT',{badarg,_}} = (catch erlang:make_tuple(5, def, [[a,b,c]])),
293    {'EXIT',{badarg,_}} = (catch erlang:make_tuple(5, def, non_list)),
294    {'EXIT',{badarg,_}} = (catch erlang:make_tuple(1 bsl 24, def, [{5,e},{1,a},{3,c}])),
295
296    ok.
297
298%% Tests the erlang:insert_element/3 BIF.
299t_insert_element(Config) when is_list(Config) ->
300    {a}       = erlang:insert_element(1, {}, a),
301    {{b,b},a} = erlang:insert_element(1, {a}, {b,b}),
302    {a,b}     = erlang:insert_element(2, {a}, b),
303    [b,def|_] = tuple_to_list(erlang:insert_element(1, erlang:make_tuple(1 bsl 20, def), b)),
304    [def,b|_] = tuple_to_list(erlang:insert_element(2, erlang:make_tuple(1 bsl 20, def), b)),
305    [def,b|_] = lists:reverse(tuple_to_list(erlang:insert_element(1 bsl 20, erlang:make_tuple(1 bsl 20, def), b))),
306    [b,def|_] = lists:reverse(tuple_to_list(erlang:insert_element((1 bsl 20) + 1, erlang:make_tuple(1 bsl 20, def), b))),
307
308    %% Error cases.
309    {'EXIT',{badarg,_}} = (catch erlang:insert_element(1, [], a)),
310    {'EXIT',{badarg,_}} = (catch erlang:insert_element(1, a, a)),
311    {'EXIT',{badarg,_}} = (catch erlang:insert_element(0, {}, a)),
312    {'EXIT',{badarg,_}} = (catch erlang:insert_element(0, {b,b,b,b,b}, a)),
313    {'EXIT',{badarg,_}} = (catch erlang:insert_element(-1, {}, a)),
314    {'EXIT',{badarg,_}} = (catch erlang:insert_element(2, {}, a)),
315    {'EXIT',{badarg,_}} = (catch erlang:insert_element(6, {b,b,b,b}, a)),
316    {'EXIT',{badarg,_}} = (catch erlang:insert_element(1 bsl 20, {b,b,b,b}, a)),
317    ok.
318
319%% Tests the erlang:delete_element/3 BIF.
320t_delete_element(Config) when is_list(Config) ->
321    {}        = erlang:delete_element(1, {a}),
322    {{b,b},c} = erlang:delete_element(1, {a,{b,b},c}),
323    {a,b}     = erlang:delete_element(2, {a,c,b}),
324    [2,3|_]   = tuple_to_list(erlang:delete_element(1, list_to_tuple(lists:seq(1, 1 bsl 20)))),
325    [1,3|_]   = tuple_to_list(erlang:delete_element(2, list_to_tuple(lists:seq(1, 1 bsl 20)))),
326    [(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))))),
327    [(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))))),
328
329    %% Error cases.
330    {'EXIT',{badarg,_}} = (catch erlang:delete_element(1, [])),
331    {'EXIT',{badarg,_}} = (catch erlang:delete_element(1, a)),
332    {'EXIT',{badarg,_}} = (catch erlang:delete_element(0, {})),
333    {'EXIT',{badarg,_}} = (catch erlang:delete_element(-1, {})),
334    {'EXIT',{badarg,_}} = (catch erlang:delete_element(1, {})),
335    {'EXIT',{badarg,_}} = (catch erlang:delete_element(0, {b,b,b,b,b})),
336    {'EXIT',{badarg,_}} = (catch erlang:delete_element(5, {b,b,b,b})),
337    {'EXIT',{badarg,_}} = (catch erlang:delete_element(1 bsl 20, {b,c,b,b,b})),
338    ok.
339
340
341%% Tests the append_element/2 BIF.
342t_append_element(Config) when is_list(Config) ->
343    ok = t_append_element({}, 2048, 2048).
344
345t_append_element_upper_boundry(Config) when is_list(Config) ->
346    sys_mem_cond_run(2048,
347		     fun () ->
348			     %% test upper boundry, 16777215 elements
349			     MaxSize  = 1 bsl 24 - 1,
350			     MaxTuple = list_to_tuple(lists:seq(1, MaxSize)),
351			     {'EXIT',{badarg,_}} = (catch erlang:append_element(MaxTuple, a)),
352			     ok
353		     end).
354
355t_append_element(_Tuple, 0, _High) -> ok;
356t_append_element(Tuple, N, High) ->
357    NewTuple = erlang:append_element(Tuple, N),
358    verify_seq(tuple_to_list(Tuple), High, N),
359    t_append_element(NewTuple, N-1, High).
360
361verify_seq([], High, High) -> ok;
362verify_seq([High], High, High) -> ok;
363verify_seq([High|T], High, Lower) ->
364    verify_seq(T, High-1, Lower).
365
366%% Tests that a case nested inside a tuple is ok.
367%% (This is known to crash earlier versions of BEAM.)
368
369tuple_with_case(Config) when is_list(Config) ->
370    {reply, true} = tuple_with_case(),
371    ok.
372
373tuple_with_case() ->
374    %% The following comments apply to the BEAM compiler.
375    foo(),                              % Reset var count.
376    {reply,                             % Compiler will choose {x,1} for tuple.
377     case foo() of                      % Call will reset var count.
378         {'EXIT', Reason} ->		% Case will return in {x,1} (first free).
379             {error, Reason};           % but the tuple will be build in {x,1},
380         _ ->                           % so case value is lost and a circular
381             true                       % data element is built.
382     end}.
383
384foo() -> ignored.
385
386%% Test to build a tuple in a guard.
387
388tuple_in_guard(Config) when is_list(Config) ->
389    Tuple1 = id({a,b}),
390    Tuple2 = id({a,b,c}),
391    if
392	Tuple1 == {element(1, Tuple2),element(2, Tuple2)} ->
393	    ok;
394	true ->
395	    ct:fail("failed")
396    end,
397    if
398	Tuple2 == {element(1, Tuple2),element(2, Tuple2),
399	    element(3, Tuple2)} ->
400	    ok;
401	true ->
402	    ct:fail("failed")
403    end,
404    ok.
405
406%% Use this function to avoid compile-time evaluation of an expression.
407id(I) -> I.
408
409
410sys_mem_cond_run(ReqSizeMB, TestFun) when is_integer(ReqSizeMB) ->
411    case total_memory() of
412	TotMem when is_integer(TotMem), TotMem >= ReqSizeMB ->
413	    TestFun();
414	TotMem when is_integer(TotMem) ->
415	    {skipped, "Not enough memory ("++integer_to_list(TotMem)++" MB)"};
416	undefined ->
417	    {skipped, "Could not retrieve memory information"}
418    end.
419
420
421total_memory() ->
422    %% Totat memory in MB.
423    try
424	MemoryData = memsup:get_system_memory_data(),
425	case lists:keysearch(total_memory, 1, MemoryData) of
426	    {value, {total_memory, TM}} ->
427		TM div (1024*1024);
428	    false ->
429		{value, {system_total_memory, STM}} =
430		    lists:keysearch(system_total_memory, 1, MemoryData),
431		STM div (1024*1024)
432	end
433    catch
434	_ : _ ->
435	    undefined
436    end.
437