%% %% %CopyrightBegin% %% %% Copyright Ericsson AB 1997-2018. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. %% You may obtain a copy of the License at %% %% http://www.apache.org/licenses/LICENSE-2.0 %% %% Unless required by applicable law or agreed to in writing, software %% distributed under the License is distributed on an "AS IS" BASIS, %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% See the License for the specific language governing permissions and %% limitations under the License. %% %% %CopyrightEnd% %% -module(tuple_SUITE). -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, t_size/1, t_tuple_size/1, t_element/1, t_setelement/1, t_insert_element/1, t_delete_element/1, t_list_to_tuple/1, t_list_to_upper_boundry_tuple/1, t_tuple_to_list/1, t_make_tuple_2/1, t_make_upper_boundry_tuple_2/1, t_make_tuple_3/1, t_append_element/1, t_append_element_upper_boundry/1, build_and_match/1, tuple_with_case/1, tuple_in_guard/1]). -include_lib("common_test/include/ct.hrl"). %% Tests tuples and the BIFs: %% %% size(Tuple) %% element/2 %% setelement/3 %% tuple_to_list/1 %% list_to_tuple/1 %% make_tuple/2 %% suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [build_and_match, t_size, t_tuple_size, t_list_to_tuple, t_list_to_upper_boundry_tuple, t_tuple_to_list, t_element, t_setelement, t_make_tuple_2, t_make_upper_boundry_tuple_2, t_make_tuple_3, t_append_element, t_append_element_upper_boundry, t_insert_element, t_delete_element, tuple_with_case, tuple_in_guard]. groups() -> []. init_per_suite(Config) -> A0 = case application:start(sasl) of ok -> [sasl]; _ -> [] end, A = case application:start(os_mon) of ok -> [os_mon|A0]; _ -> A0 end, [{started_apps, A}|Config]. end_per_suite(Config) -> As = proplists:get_value(started_apps, Config), lists:foreach(fun (A) -> application:stop(A) end, As), Config. init_per_group(_GroupName, Config) -> Config. end_per_group(_GroupName, Config) -> Config. build_and_match(Config) when is_list(Config) -> {} = id({}), {1} = id({1}), {1, 2} = id({1, 2}), {1, 2, 3} = id({1, 2, 3}), {1, 2, 3, 4} = id({1, 2, 3, 4}), {1, 2, 3, 4, 5} = id({1, 2, 3, 4, 5}), {1, 2, 3, 4, 5, 6} = id({1, 2, 3, 4, 5, 6}), {1, 2, 3, 4, 5, 6} = id({1, 2, 3, 4, 5, 6}), {1, 2, 3, 4, 5, 6, 7} = id({1, 2, 3, 4, 5, 6, 7}), {1, 2, 3, 4, 5, 6, 7, 8} = id({1, 2, 3, 4, 5, 6, 7, 8}), ok. %% Tests size(Tuple). t_size(Config) when is_list(Config) -> 0 = size({}), 1 = size({a}), 1 = size({{a}}), 2 = size({{a}, {b}}), 3 = size({1, 2, 3}), ok. t_tuple_size(Config) when is_list(Config) -> 0 = tuple_size(id({})), 1 = tuple_size(id({a})), 1 = tuple_size(id({{a}})), 2 = tuple_size(id({{a},{b}})), 3 = tuple_size(id({1,2,3})), %% Error cases. {'EXIT',{badarg,_}} = (catch tuple_size([])), {'EXIT',{badarg,_}} = (catch tuple_size(<<1,2,3>>)), error = ludicrous_tuple_size({a,b,c}), error = ludicrous_tuple_size([a,b,c]), ok. ludicrous_tuple_size(T) when tuple_size(T) =:= 16#7777777777777777777777777777777777 -> ok; ludicrous_tuple_size(_) -> error. %% Tests element/2. t_element(Config) when is_list(Config) -> a = element(1, {a}), a = element(1, {a, b}), List = lists:seq(1, 4096), Tuple = list_to_tuple(lists:seq(1, 4096)), get_elements(List, Tuple, 1), {'EXIT', {badarg, _}} = (catch element(0, id({a,b}))), {'EXIT', {badarg, _}} = (catch element(3, id({a,b}))), {'EXIT', {badarg, _}} = (catch element(1, id({}))), {'EXIT', {badarg, _}} = (catch element(1, id([a,b]))), {'EXIT', {badarg, _}} = (catch element(1, id(42))), {'EXIT', {badarg, _}} = (catch element(id(1.5), id({a,b}))), %% Make sure that the loader does not reject the module when %% huge literal index values are used. {'EXIT', {badarg, _}} = (catch element((1 bsl 24)-1, id({a,b,c}))), {'EXIT', {badarg, _}} = (catch element(1 bsl 24, id({a,b,c}))), {'EXIT', {badarg, _}} = (catch element(1 bsl 32, id({a,b,c}))), {'EXIT', {badarg, _}} = (catch element(1 bsl 64, id({a,b,c}))), ok. get_elements([Element|Rest], Tuple, Pos) -> Element = element(Pos, Tuple), get_elements(Rest, Tuple, Pos+1); get_elements([], _Tuple, _Pos) -> ok. %% Tests set_element/3. t_setelement(Config) when is_list(Config) -> {x} = setelement(1, id({1}), x), {x,2} = setelement(1, id({1,2}), x), {1,x} = setelement(2, id({1,2}), x), Tuple = list_to_tuple(lists:duplicate(2048, x)), NewTuple = set_all_elements(Tuple, 1), NewTuple = list_to_tuple(lists:seq(1+7, 2048+7)), {'EXIT', {badarg, _}} = (catch setelement(0, {a, b}, x)), {'EXIT', {badarg, _}} = (catch setelement(3, {a, b}, x)), {'EXIT', {badarg, _}} = (catch setelement(1, {}, x)), {'EXIT', {badarg, _}} = (catch setelement(1, [a, b], x)), {'EXIT', {badarg, _}} = (catch setelement(1.5, {a, b}, x)), %% Nested setelement with literals. AnotherTuple = id({0,0,a,b,c}), {93748793749387837476555412,3.0,gurka,b,c} = setelement(1, setelement(2, setelement(3, AnotherTuple, gurka), 3.0), 93748793749387837476555412), ok. set_all_elements(Tuple, Pos) when Pos =< size(Tuple) -> set_all_elements(setelement(Pos, Tuple, Pos+7), Pos+1); set_all_elements(Tuple, Pos) when Pos > size(Tuple) -> Tuple. %% Tests list_to_tuple/1. t_list_to_tuple(Config) when is_list(Config) -> {} = list_to_tuple([]), {a} = list_to_tuple([a]), {a, b} = list_to_tuple([a, b]), {a, b, c} = list_to_tuple([a, b, c]), {a, b, c, d} = list_to_tuple([a, b, c, d]), {a, b, c, d, e} = list_to_tuple([a, b, c, d, e]), Size = 4096, Tuple = list_to_tuple(lists:seq(1, Size)), Size = size(Tuple), {'EXIT', {badarg, _}} = (catch list_to_tuple(id({a,b}))), {'EXIT', {badarg, _}} = (catch list_to_tuple(id([a|b]))), {'EXIT', {badarg, _}} = (catch list_to_tuple(id([a|b]))), {'EXIT', {badarg,_}} = (catch list_to_tuple(lists:seq(1, 1 bsl 24))), ok. t_list_to_upper_boundry_tuple(Config) when is_list(Config) -> sys_mem_cond_run(2048, fun () -> %% test upper boundry, 16777215 elements MaxSize = 1 bsl 24 - 1, MaxTuple = list_to_tuple(lists:seq(1, MaxSize)), MaxSize = size(MaxTuple), ok end). %% Tests tuple_to_list/1. t_tuple_to_list(Config) when is_list(Config) -> [] = tuple_to_list({}), [a] = tuple_to_list({a}), [a, b] = tuple_to_list({a, b}), [a, b, c] = tuple_to_list({a, b, c}), [a, b, c, d] = tuple_to_list({a, b, c, d}), [a, b, c, d] = tuple_to_list({a, b, c, d}), Size = 4096, List = lists:seq(1, Size), Tuple = list_to_tuple(List), Size = size(Tuple), List = tuple_to_list(Tuple), {'EXIT', {badarg,_}} = (catch tuple_to_list(id(a))), {'EXIT', {badarg,_}} = (catch tuple_to_list(id(42))), ok. %% Tests the make_tuple/2 BIF. t_make_tuple_2(Config) when is_list(Config) -> t_make_tuple1([]), t_make_tuple1(42), t_make_tuple1(a), t_make_tuple1({}), t_make_tuple1({a}), t_make_tuple1(erlang:make_tuple(400, [])), {'EXIT', {badarg,_}} = (catch erlang:make_tuple(1 bsl 24, a)), {'EXIT', {badarg,_}} = (catch erlang:make_tuple(-1, a)), % 26 bits is the total header arity room (for now) {'EXIT', {badarg,_}} = (catch erlang:make_tuple(1 bsl 26 + 3, a)), % bignum {'EXIT', {badarg,_}} = (catch erlang:make_tuple(1 bsl 65 + 3, a)), ok. t_make_upper_boundry_tuple_2(Config) when is_list(Config) -> sys_mem_cond_run(2048, fun () -> %% test upper boundry, 16777215 elements t_make_tuple(1 bsl 24 - 1, a) end). t_make_tuple1(Element) -> lists:foreach(fun(Size) -> t_make_tuple(Size, Element) end, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 255, 256, 511, 512, 999, 1000, 1023, 1024, 4095, 4096]). t_make_tuple(Size, Element) -> Tuple = erlang:make_tuple(Size, Element), lists:foreach(fun(El) when El =:= Element -> ok; (Other) -> ct:fail({got, Other, expected, Element}) end, tuple_to_list(Tuple)). %% Tests the erlang:make_tuple/3 BIF. t_make_tuple_3(Config) when is_list(Config) -> {} = erlang:make_tuple(0, def, []), {def} = erlang:make_tuple(1, def, []), {a} = erlang:make_tuple(1, def, [{1,a}]), {a,def,c,def,e} = erlang:make_tuple(5, def, [{5,e},{1,a},{3,c}]), {a,def,c,def,e} = erlang:make_tuple(5, def, [{1,blurf},{5,e},{3,blurf},{1,a},{3,c}]), MaxSize = 1 bsl 16 - 1, MaxTuple = erlang:make_tuple(MaxSize, def, [{1,blurf},{5,e},{3,blurf},{1,a},{3,c}]), MaxSize = size(MaxTuple), %% Error cases. {'EXIT',{badarg,_}} = (catch erlang:make_tuple(0, def, [{1,a}])), {'EXIT',{badarg,_}} = (catch erlang:make_tuple(5, def, [{-1,a}])), {'EXIT',{badarg,_}} = (catch erlang:make_tuple(5, def, [{0,a}])), {'EXIT',{badarg,_}} = (catch erlang:make_tuple(5, def, [{6,z}])), {'EXIT',{badarg,_}} = (catch erlang:make_tuple(a, def, [{6,z}])), {'EXIT',{badarg,_}} = (catch erlang:make_tuple(5, def, [{1,a}|b])), {'EXIT',{badarg,_}} = (catch erlang:make_tuple(5, def, [42])), {'EXIT',{badarg,_}} = (catch erlang:make_tuple(5, def, [[a,b,c]])), {'EXIT',{badarg,_}} = (catch erlang:make_tuple(5, def, non_list)), {'EXIT',{badarg,_}} = (catch erlang:make_tuple(1 bsl 24, def, [{5,e},{1,a},{3,c}])), ok. %% Tests the erlang:insert_element/3 BIF. t_insert_element(Config) when is_list(Config) -> {a} = erlang:insert_element(1, {}, a), {{b,b},a} = erlang:insert_element(1, {a}, {b,b}), {a,b} = erlang:insert_element(2, {a}, b), [b,def|_] = tuple_to_list(erlang:insert_element(1, erlang:make_tuple(1 bsl 20, def), b)), [def,b|_] = tuple_to_list(erlang:insert_element(2, erlang:make_tuple(1 bsl 20, def), b)), [def,b|_] = lists:reverse(tuple_to_list(erlang:insert_element(1 bsl 20, erlang:make_tuple(1 bsl 20, def), b))), [b,def|_] = lists:reverse(tuple_to_list(erlang:insert_element((1 bsl 20) + 1, erlang:make_tuple(1 bsl 20, def), b))), %% Error cases. {'EXIT',{badarg,_}} = (catch erlang:insert_element(1, [], a)), {'EXIT',{badarg,_}} = (catch erlang:insert_element(1, a, a)), {'EXIT',{badarg,_}} = (catch erlang:insert_element(0, {}, a)), {'EXIT',{badarg,_}} = (catch erlang:insert_element(0, {b,b,b,b,b}, a)), {'EXIT',{badarg,_}} = (catch erlang:insert_element(-1, {}, a)), {'EXIT',{badarg,_}} = (catch erlang:insert_element(2, {}, a)), {'EXIT',{badarg,_}} = (catch erlang:insert_element(6, {b,b,b,b}, a)), {'EXIT',{badarg,_}} = (catch erlang:insert_element(1 bsl 20, {b,b,b,b}, a)), ok. %% Tests the erlang:delete_element/3 BIF. t_delete_element(Config) when is_list(Config) -> {} = erlang:delete_element(1, {a}), {{b,b},c} = erlang:delete_element(1, {a,{b,b},c}), {a,b} = erlang:delete_element(2, {a,c,b}), [2,3|_] = tuple_to_list(erlang:delete_element(1, list_to_tuple(lists:seq(1, 1 bsl 20)))), [1,3|_] = tuple_to_list(erlang:delete_element(2, list_to_tuple(lists:seq(1, 1 bsl 20)))), [(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))))), [(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))))), %% Error cases. {'EXIT',{badarg,_}} = (catch erlang:delete_element(1, [])), {'EXIT',{badarg,_}} = (catch erlang:delete_element(1, a)), {'EXIT',{badarg,_}} = (catch erlang:delete_element(0, {})), {'EXIT',{badarg,_}} = (catch erlang:delete_element(-1, {})), {'EXIT',{badarg,_}} = (catch erlang:delete_element(1, {})), {'EXIT',{badarg,_}} = (catch erlang:delete_element(0, {b,b,b,b,b})), {'EXIT',{badarg,_}} = (catch erlang:delete_element(5, {b,b,b,b})), {'EXIT',{badarg,_}} = (catch erlang:delete_element(1 bsl 20, {b,c,b,b,b})), ok. %% Tests the append_element/2 BIF. t_append_element(Config) when is_list(Config) -> ok = t_append_element({}, 2048, 2048). t_append_element_upper_boundry(Config) when is_list(Config) -> sys_mem_cond_run(2048, fun () -> %% test upper boundry, 16777215 elements MaxSize = 1 bsl 24 - 1, MaxTuple = list_to_tuple(lists:seq(1, MaxSize)), {'EXIT',{badarg,_}} = (catch erlang:append_element(MaxTuple, a)), ok end). t_append_element(_Tuple, 0, _High) -> ok; t_append_element(Tuple, N, High) -> NewTuple = erlang:append_element(Tuple, N), verify_seq(tuple_to_list(Tuple), High, N), t_append_element(NewTuple, N-1, High). verify_seq([], High, High) -> ok; verify_seq([High], High, High) -> ok; verify_seq([High|T], High, Lower) -> verify_seq(T, High-1, Lower). %% Tests that a case nested inside a tuple is ok. %% (This is known to crash earlier versions of BEAM.) tuple_with_case(Config) when is_list(Config) -> {reply, true} = tuple_with_case(), ok. tuple_with_case() -> %% The following comments apply to the BEAM compiler. foo(), % Reset var count. {reply, % Compiler will choose {x,1} for tuple. case foo() of % Call will reset var count. {'EXIT', Reason} -> % Case will return in {x,1} (first free). {error, Reason}; % but the tuple will be build in {x,1}, _ -> % so case value is lost and a circular true % data element is built. end}. foo() -> ignored. %% Test to build a tuple in a guard. tuple_in_guard(Config) when is_list(Config) -> Tuple1 = id({a,b}), Tuple2 = id({a,b,c}), if Tuple1 == {element(1, Tuple2),element(2, Tuple2)} -> ok; true -> ct:fail("failed") end, if Tuple2 == {element(1, Tuple2),element(2, Tuple2), element(3, Tuple2)} -> ok; true -> ct:fail("failed") end, ok. %% Use this function to avoid compile-time evaluation of an expression. id(I) -> I. sys_mem_cond_run(ReqSizeMB, TestFun) when is_integer(ReqSizeMB) -> case total_memory() of TotMem when is_integer(TotMem), TotMem >= ReqSizeMB -> TestFun(); TotMem when is_integer(TotMem) -> {skipped, "Not enough memory ("++integer_to_list(TotMem)++" MB)"}; undefined -> {skipped, "Could not retrieve memory information"} end. total_memory() -> %% Totat memory in MB. try MemoryData = memsup:get_system_memory_data(), case lists:keysearch(total_memory, 1, MemoryData) of {value, {total_memory, TM}} -> TM div (1024*1024); false -> {value, {system_total_memory, STM}} = lists:keysearch(system_total_memory, 1, MemoryData), STM div (1024*1024) end catch _ : _ -> undefined end.