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