diff options
author | Erlang/OTP <[email protected]> | 2009-11-20 14:54:40 +0000 |
---|---|---|
committer | Erlang/OTP <[email protected]> | 2009-11-20 14:54:40 +0000 |
commit | 84adefa331c4159d432d22840663c38f155cd4c1 (patch) | |
tree | bff9a9c66adda4df2106dfd0e5c053ab182a12bd /erts/emulator/test/binary_SUITE.erl | |
download | otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.bz2 otp-84adefa331c4159d432d22840663c38f155cd4c1.zip |
The R13B03 release.OTP_R13B03
Diffstat (limited to 'erts/emulator/test/binary_SUITE.erl')
-rw-r--r-- | erts/emulator/test/binary_SUITE.erl | 1313 |
1 files changed, 1313 insertions, 0 deletions
diff --git a/erts/emulator/test/binary_SUITE.erl b/erts/emulator/test/binary_SUITE.erl new file mode 100644 index 0000000000..e47dfa18f7 --- /dev/null +++ b/erts/emulator/test/binary_SUITE.erl @@ -0,0 +1,1313 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +-module(binary_SUITE). +-compile({nowarn_deprecated_function, {erlang,hash,2}}). + +%% Tests binaries and the BIFs: +%% list_to_binary/1 +%% iolist_to_binary/1 +%% bitstr_to_list/1 +%% binary_to_list/1 +%% binary_to_list/3 +%% binary_to_term/1 +%% bitstr_to_list/1 +%% term_to_binary/1 +%% erlang:external_size/1 +%% size(Binary) +%% iolist_size/1 +%% concat_binary/1 +%% split_binary/2 +%% hash(Binary, N) +%% phash(Binary, N) +%% phash2(Binary, N) +%% + +-include("test_server.hrl"). + +-export([all/1, init_per_testcase/2, fin_per_testcase/2, + copy_terms/1, conversions/1, deep_lists/1, deep_bitstr_lists/1, + bad_list_to_binary/1, bad_binary_to_list/1, + t_split_binary/1, bad_split/1, t_concat_binary/1, + terms/1, terms_float/1, external_size/1, t_iolist_size/1, + t_hash/1, + bad_size/1, + bad_term_to_binary/1, + bad_binary_to_term_2/1, + bad_binary_to_term/1, bad_terms/1, more_bad_terms/1, + otp_5484/1,otp_5933/1, + ordering/1,unaligned_order/1,gc_test/1, + bit_sized_binary_sizes/1, + bitlevel_roundtrip/1, + otp_6817/1,deep/1,obsolete_funs/1,robustness/1,otp_8117/1, + otp_8180/1]). + +%% Internal exports. +-export([sleeper/0]). + +all(suite) -> + [copy_terms,conversions,deep_lists,deep_bitstr_lists, + t_split_binary, bad_split, t_concat_binary, + bad_list_to_binary, bad_binary_to_list, terms, terms_float, + external_size, t_iolist_size, + bad_binary_to_term_2, + bad_binary_to_term, bad_terms, t_hash, bad_size, bad_term_to_binary, + more_bad_terms, otp_5484, otp_5933, ordering, unaligned_order, + gc_test, bit_sized_binary_sizes, bitlevel_roundtrip, otp_6817, otp_8117, + deep,obsolete_funs,robustness,otp_8180]. + +init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) -> + Dog=?t:timetrap(?t:minutes(2)), + [{watchdog, Dog}|Config]. + +fin_per_testcase(_Func, Config) -> + Dog=?config(watchdog, Config), + ?t:timetrap_cancel(Dog). + +-define(heap_binary_size, 64). + +copy_terms(Config) when is_list(Config) -> + Self = self(), + ?line Pid = spawn_link(fun() -> copy_server(Self) end), + F = fun(Term) -> + Pid ! Term, + receive + Term -> ok; + Other -> + io:format("Sent: ~P\nGot back:~P", [Term,12,Other,12]), + ?t:fail(bad_term) + end + end, + ?line test_terms(F), + ok. + +copy_server(Parent) -> + receive + Term -> + Parent ! Term, + copy_server(Parent) + end. + +%% Tests list_to_binary/1, binary_to_list/1 and size/1, +%% using flat lists. + +conversions(suite) -> []; +conversions(Config) when is_list(Config) -> + ?line test_bin([]), + ?line test_bin([1]), + ?line test_bin([1, 2]), + ?line test_bin([1, 2, 3]), + ?line test_bin(lists:seq(0, ?heap_binary_size)), + ?line test_bin(lists:seq(0, ?heap_binary_size+1)), + ?line test_bin(lists:seq(0, 255)), + ?line test_bin(lists:duplicate(50000, $@)), + + %% Binary in list. + List = [1,2,3,4,5], + ?line B1 = make_sub_binary(list_to_binary(List)), + ?line 5 = size(B1), + ?line 5 = size(make_unaligned_sub_binary(B1)), + ?line 40 = bit_size(B1), + ?line 40 = bit_size(make_unaligned_sub_binary(B1)), + ?line B2 = list_to_binary([42,B1,19]), + ?line B2 = list_to_binary([42,make_unaligned_sub_binary(B1),19]), + ?line B2 = iolist_to_binary(B2), + ?line B2 = iolist_to_binary(make_unaligned_sub_binary(B2)), + ?line 7 = size(B2), + ?line 7 = size(make_sub_binary(B2)), + ?line 56 = bit_size(B2), + ?line 56 = bit_size(make_sub_binary(B2)), + ?line [42,1,2,3,4,5,19] = binary_to_list(B2), + ?line [42,1,2,3,4,5,19] = binary_to_list(make_sub_binary(B2)), + ?line [42,1,2,3,4,5,19] = binary_to_list(make_unaligned_sub_binary(B2)), + ?line [42,1,2,3,4,5,19] = bitstring_to_list(B2), + ?line [42,1,2,3,4,5,19] = bitstring_to_list(make_sub_binary(B2)), + ?line [42,1,2,3,4,5,19] = bitstring_to_list(make_unaligned_sub_binary(B2)), + + ok. + +test_bin(List) -> + ?line Size = length(List), + ?line Bin = list_to_binary(List), + ?line Bin = iolist_to_binary(List), + ?line Bin = list_to_bitstring(List), + ?line Size = iolist_size(List), + ?line Size = iolist_size(Bin), + ?line Size = iolist_size(make_unaligned_sub_binary(Bin)), + ?line Size = size(Bin), + ?line Size = size(make_sub_binary(Bin)), + ?line Size = size(make_unaligned_sub_binary(Bin)), + ?line List = binary_to_list(Bin), + ?line List = binary_to_list(make_sub_binary(Bin)), + ?line List = binary_to_list(make_unaligned_sub_binary(Bin)), + ?line List = bitstring_to_list(Bin), + ?line List = bitstring_to_list(make_unaligned_sub_binary(Bin)). + +%% Tests list_to_binary/1, iolist_to_binary/1, list_to_bitstr/1, binary_to_list/1,3, +%% bitstr_to_list/1, and size/1, using deep lists. + +deep_lists(Config) when is_list(Config) -> + ?line test_deep_list(["abc"]), + ?line test_deep_list([[12,13,[123,15]]]), + ?line test_deep_list([[12,13,[lists:seq(0, 255), []]]]), + ok. + +test_deep_list(List) -> + ?line FlatList = lists:flatten(List), + ?line Size = length(FlatList), + ?line Bin = list_to_binary(List), + ?line Bin = iolist_to_binary(List), + ?line Bin = iolist_to_binary(Bin), + ?line Bin = list_to_bitstring(List), + ?line Size = size(Bin), + ?line Size = iolist_size(List), + ?line Size = iolist_size(FlatList), + ?line Size = iolist_size(Bin), + ?line Bitsize = bit_size(Bin), + ?line Bitsize = 8*Size, + ?line FlatList = binary_to_list(Bin), + ?line FlatList = bitstring_to_list(Bin), + io:format("testing plain binary..."), + ?line t_binary_to_list_3(FlatList, Bin, 1, Size), + io:format("testing unaligned sub binary..."), + ?line t_binary_to_list_3(FlatList, make_unaligned_sub_binary(Bin), 1, Size). + +t_binary_to_list_3(List, Bin, From, To) -> + ?line going_up(List, Bin, From, To), + ?line going_down(List, Bin, From, To), + ?line going_center(List, Bin, From, To). + +going_up(List, Bin, From, To) when From =< To -> + ?line List = binary_to_list(Bin, From, To), + ?line going_up(tl(List), Bin, From+1, To); +going_up(_List, _Bin, From, To) when From > To -> + ok. + +going_down(List, Bin, From, To) when To > 0-> + ?line compare(List, binary_to_list(Bin, From, To), To-From+1), + ?line going_down(List, Bin, From, To-1); +going_down(_List, _Bin, _From, _To) -> + ok. + +going_center(List, Bin, From, To) when From >= To -> + ?line compare(List, binary_to_list(Bin, From, To), To-From+1), + ?line going_center(tl(List), Bin, From+1, To-1); +going_center(_List, _Bin, _From, _To) -> + ok. + +compare([X|Rest1], [X|Rest2], Left) when Left > 0 -> + ?line compare(Rest1, Rest2, Left-1); +compare([_X|_], [_Y|_], _Left) -> + ?line test_server:fail(); +compare(_List, [], 0) -> + ok. + +deep_bitstr_lists(Config) when is_list(Config) -> + ?line {<<7:3>>,[<<7:3>>]} = test_deep_bitstr([<<7:3>>]), + ?line {<<42,5:3>>=Bin,[42,<<5:3>>]=List} = test_deep_bitstr([42,<<5:3>>]), + ?line {Bin,List} = test_deep_bitstr([42|<<5:3>>]), + ?line {Bin,List} = test_deep_bitstr([<<42,5:3>>]), + ?line {Bin,List} = test_deep_bitstr([<<1:3>>,<<10:5>>|<<5:3>>]), + ?line {Bin,List} = test_deep_bitstr([<<1:3>>,<<10:5>>,<<5:3>>]), + ?line {Bin,List} = test_deep_bitstr([[<<1:3>>,<<10:5>>],[],<<5:3>>]), + ?line {Bin,List} = test_deep_bitstr([[[<<1:3>>]|<<10:5>>],[],<<5:3>>]), + ?line {Bin,List} = test_deep_bitstr([[<<0:1>>,<<0:1>>,[],<<1:1>>,<<10:5>>], + <<1:1>>,<<0:1>>,<<1:1>>]), + ok. + +test_deep_bitstr(List) -> + %%?line {'EXIT',{badarg,_}} = list_to_binary(List), + Bin = list_to_bitstring(List), + {Bin,bitstring_to_list(Bin)}. + +bad_list_to_binary(suite) -> []; +bad_list_to_binary(Config) when is_list(Config) -> + ?line test_bad_bin(atom), + ?line test_bad_bin(42), + ?line test_bad_bin([1|2]), + ?line test_bad_bin([256]), + ?line test_bad_bin([255, [256]]), + ?line test_bad_bin([-1]), + ?line test_bad_bin([atom_in_list]), + ?line test_bad_bin([[<<8>>]|bad_tail]), + + {'EXIT',{badarg,_}} = (catch list_to_binary(id(<<1,2,3>>))), + {'EXIT',{badarg,_}} = (catch list_to_binary(id([<<42:7>>]))), + {'EXIT',{badarg,_}} = (catch list_to_bitstring(id(<<1,2,3>>))), + + %% Funs used to be implemented as a type of binary internally. + ?line test_bad_bin(fun(X, Y) -> X*Y end), + ?line test_bad_bin([1,fun(X) -> X + 1 end,2|fun() -> 0 end]), + ?line test_bad_bin([fun(X) -> X + 1 end]), + ok. + +test_bad_bin(List) -> + {'EXIT',{badarg,_}} = (catch list_to_binary(List)), + {'EXIT',{badarg,_}} = (catch iolist_to_binary(List)), + {'EXIT',{badarg,_}} = (catch list_to_bitstring(List)). + +bad_binary_to_list(doc) -> "Tries binary_to_list/1,3 with bad arguments."; +bad_binary_to_list(Config) when is_list(Config) -> + ?line bad_bin_to_list(fun(X) -> X * 42 end), + + GoodBin = list_to_binary(lists:seq(1, 10)), + ?line bad_bin_to_list(fun(X) -> X * 44 end, 1, 2), + ?line bad_bin_to_list(GoodBin, 0, 1), + ?line bad_bin_to_list(GoodBin, 2, 1), + ?line bad_bin_to_list(GoodBin, 11, 11), + {'EXIT',{badarg,_}} = (catch binary_to_list(id(<<42:7>>))), + ok. + +bad_bin_to_list(BadBin) -> + {'EXIT',{badarg,_}} = (catch binary_to_list(BadBin)), + {'EXIT',{badarg,_}} = (catch bitstring_to_list(BadBin)). + +bad_bin_to_list(Bin, First, Last) -> + {'EXIT',{badarg,_}} = (catch binary_to_list(Bin, First, Last)). + + +%% Tries to split a binary at all possible positions. + +t_split_binary(suite) -> []; +t_split_binary(Config) when is_list(Config) -> + ?line L = lists:seq(0, ?heap_binary_size-5), %Heap binary. + ?line B = list_to_binary(L), + ?line split(L, B, size(B)), + + %% Sub binary of heap binary. + ?line split(L, make_sub_binary(B), size(B)), + {X,_Y} = split_binary(B, size(B) div 2), + ?line split(binary_to_list(X), X, size(X)), + + %% Unaligned sub binary of heap binary. + ?line split(L, make_unaligned_sub_binary(B), size(B)), + {X,_Y} = split_binary(B, size(B) div 2), + ?line split(binary_to_list(X), X, size(X)), + + %% Reference-counted binary. + ?line L2 = lists:seq(0, ?heap_binary_size+1), + ?line B2 = list_to_binary(L2), + ?line split(L2, B2, size(B2)), + + %% Sub binary of reference-counted binary. + ?line split(L2, make_sub_binary(B2), size(B2)), + {X2,_Y2} = split_binary(B2, size(B2) div 2), + ?line split(binary_to_list(X2), X2, size(X2)), + + %% Unaligned sub binary of reference-counted binary. + ?line split(L2, make_unaligned_sub_binary(B2), size(B2)), + {X2,_Y2} = split_binary(B2, size(B2) div 2), + ?line split(binary_to_list(X2), X2, size(X2)), + + ok. + +split(L, B, Pos) when Pos > 0 -> + ?line {B1, B2} = split_binary(B, Pos), + ?line B1 = list_to_binary(lists:sublist(L, 1, Pos)), + ?line B2 = list_to_binary(lists:nthtail(Pos, L)), + ?line split(L, B, Pos-1); +split(_L, _B, 0) -> + ok. + +bad_split(doc) -> "Tries split_binary/2 with bad arguments."; +bad_split(suite) -> []; +bad_split(Config) when is_list(Config) -> + GoodBin = list_to_binary([1,2,3]), + ?line bad_split(GoodBin, -1), + ?line bad_split(GoodBin, 4), + ?line bad_split(GoodBin, a), + + %% Funs are a kind of binaries. + ?line bad_split(fun(_X) -> 1 end, 1), + ok. + +bad_split(Bin, Pos) -> + {'EXIT',{badarg,_}} = (catch split_binary(Bin, Pos)). + +%% Tests concat_binary/2 and size/1. + +t_concat_binary(suite) -> []; +t_concat_binary(Config) when is_list(Config) -> + test_concat([]), + + test_concat([[]]), + test_concat([[], []]), + test_concat([[], [], []]), + + test_concat([[1], []]), + test_concat([[], [2]]), + test_concat([[], [3], []]), + + test_concat([[1, 2, 3], [4, 5, 6, 7]]), + test_concat([[1, 2, 3], [4, 5, 6, 7], [9, 10]]), + + test_concat([lists:seq(0, 255), lists:duplicate(1024, $@), + lists:duplicate(2048, $a), + lists:duplicate(4000, $b)]), + ok. + +test_concat(Lists) -> + test_concat(Lists, 0, [], []). + +test_concat([List|Rest], Size, Combined, Binaries) -> + ?line Bin = list_to_binary(List), + ?line test_concat(Rest, Size+length(List), Combined++List, [Bin|Binaries]); +test_concat([], Size, Combined, Binaries0) -> + ?line Binaries = lists:reverse(Binaries0), + ?line Bin = concat_binary(Binaries), + ?line Size = size(Bin), + ?line Size = iolist_size(Bin), + ?line Combined = binary_to_list(Bin). + +t_hash(doc) -> "Test hash/2 with different type of binaries."; +t_hash(Config) when is_list(Config) -> + test_hash([]), + test_hash([253]), + test_hash(lists:seq(1, ?heap_binary_size)), + test_hash(lists:seq(1, ?heap_binary_size+1)), + test_hash([X rem 256 || X <- lists:seq(1, 312)]), + ok. + +test_hash(List) -> + Bin = list_to_binary(List), + Sbin = make_sub_binary(List), + Unaligned = make_unaligned_sub_binary(Sbin), + ?line test_hash_1(Bin, Sbin, Unaligned, fun erlang:hash/2), + ?line test_hash_1(Bin, Sbin, Unaligned, fun erlang:phash/2), + ?line test_hash_1(Bin, Sbin, Unaligned, fun erlang:phash2/2). + +test_hash_1(Bin, Sbin, Unaligned, Hash) when is_function(Hash, 2) -> + N = 65535, + case {Hash(Bin, N),Hash(Sbin, N),Hash(Unaligned, N)} of + {H,H,H} -> ok; + {H1,H2,H3} -> + io:format("Different hash values: ~p, ~p, ~p\n", [H1,H2,H3]), + ?t:fail() + end. + +bad_size(doc) -> "Try bad arguments to size/1."; +bad_size(suite) -> []; +bad_size(Config) when is_list(Config) -> + ?line {'EXIT',{badarg,_}} = (catch size(fun(X) -> X + 33 end)), + ok. + +bad_term_to_binary(Config) when is_list(Config) -> + T = id({a,b,c}), + ?line {'EXIT',{badarg,_}} = (catch term_to_binary(T, not_a_list)), + ?line {'EXIT',{badarg,_}} = (catch term_to_binary(T, [blurf])), + ?line {'EXIT',{badarg,_}} = (catch term_to_binary(T, [{compressed,-1}])), + ?line {'EXIT',{badarg,_}} = (catch term_to_binary(T, [{compressed,10}])), + ?line {'EXIT',{badarg,_}} = (catch term_to_binary(T, [{compressed,cucumber}])), + ?line {'EXIT',{badarg,_}} = (catch term_to_binary(T, [{compressed}])), + ?line {'EXIT',{badarg,_}} = (catch term_to_binary(T, [{version,1}|bad_tail])), + ?line {'EXIT',{badarg,_}} = (catch term_to_binary(T, [{minor_version,-1}])), + ?line {'EXIT',{badarg,_}} = (catch term_to_binary(T, [{minor_version,x}])), + + ok. + +%% Tests binary_to_term/1 and term_to_binary/1. + +terms(Config) when is_list(Config) -> + TestFun = fun(Term) -> + try + S = io_lib:format("~p", [Term]), + io:put_chars(S) + catch + error:badarg -> + io:put_chars("bit sized binary") + end, + Bin = term_to_binary(Term), + case erlang:external_size(Bin) of + Sz when is_integer(Sz), size(Bin) =< Sz -> + ok + end, + Term = binary_to_term(Bin), + Unaligned = make_unaligned_sub_binary(Bin), + Term = binary_to_term(Unaligned), + BinC = erlang:term_to_binary(Term, [compressed]), + Term = binary_to_term(BinC), + true = size(BinC) =< size(Bin), + Bin = term_to_binary(Term, [{compressed,0}]), + terms_compression_levels(Term, size(Bin), 1), + UnalignedC = make_unaligned_sub_binary(BinC), + Term = binary_to_term(UnalignedC) + end, + ?line test_terms(TestFun), + ok. + +terms_compression_levels(Term, UncompressedSz, Level) when Level < 10 -> + BinC = erlang:term_to_binary(Term, [{compressed,Level}]), + Term = binary_to_term(BinC), + Sz = byte_size(BinC), + true = Sz =< UncompressedSz, + terms_compression_levels(Term, UncompressedSz, Level+1); +terms_compression_levels(_, _, _) -> ok. + +terms_float(Config) when is_list(Config) -> + ?line test_floats(fun(Term) -> + Bin0 = term_to_binary(Term), + Bin0 = term_to_binary(Term, [{minor_version,0}]), + Term = binary_to_term(Bin0), + Bin1 = term_to_binary(Term, [{minor_version,1}]), + Term = binary_to_term(Bin1), + true = size(Bin1) < size(Bin0) + end). + +external_size(Config) when is_list(Config) -> + %% Build a term whose external size only fits in a big num (on 32-bit CPU). + ?line external_size_1(16#11111111111111117777777777777777888889999, 0, 16#FFFFFFF), + + %% Test that the same binary aligned and unaligned has the same external size. + ?line Bin = iolist_to_binary([1,2,3,96]), + ?line Unaligned = make_unaligned_sub_binary(Bin), + case {erlang:external_size(Bin),erlang:external_size(Unaligned)} of + {X,X} -> ok; + {Sz1,Sz2} -> + io:format(" Aligned size: ~p\n", [Sz1]), + io:format("Unaligned size: ~p\n", [Sz2]), + ?line ?t:fail() + end. + +external_size_1(Term, Size0, Limit) when Size0 < Limit -> + case erlang:external_size(Term) of + Size when is_integer(Size), Size0 < Size -> + io:format("~p", [Size]), + external_size_1([Term|Term], Size, Limit) + end; +external_size_1(_, _, _) -> ok. + +t_iolist_size(Config) when is_list(Config) -> + %% Build a term whose external size only fits in a big num (on 32-bit CPU). + Bin = iolist_to_binary(lists:seq(0, 254)), + ?line ok = t_iolist_size_1(Bin, 0, 16#7FFFFFFF), + ?line ok = t_iolist_size_1(make_unaligned_sub_binary(Bin), 0, 16#7FFFFFFF). + +t_iolist_size_1(IOList, Size0, Limit) when Size0 < Limit -> + case iolist_size(IOList) of + Size when is_integer(Size), Size0 < Size -> + io:format("~p", [Size]), + t_iolist_size_1([IOList|IOList], Size, Limit) + end; +t_iolist_size_1(_, _, _) -> ok. + +bad_binary_to_term_2(doc) -> "OTP-4053."; +bad_binary_to_term_2(suite) -> []; +bad_binary_to_term_2(Config) when is_list(Config) -> + ?line {ok, N} = test_server:start_node(plopp, slave, []), + ?line R = rpc:call(N, erlang, binary_to_term, [<<131,111,255,255,255,0>>]), + ?line case R of + {badrpc, {'EXIT', _}} -> + ok; + _Other -> + test_server:fail({rpcresult, R}) + end, + ?line test_server:stop_node(N), + ok. + +bad_binary_to_term(doc) -> "Try bad input to binary_to_term/1."; +bad_binary_to_term(Config) when is_list(Config) -> + ?line bad_bin_to_term(an_atom), + ?line bad_bin_to_term({an,tuple}), + ?line bad_bin_to_term({a,list}), + ?line bad_bin_to_term(fun() -> self() end), + ?line bad_bin_to_term(fun(X) -> 42*X end), + ?line bad_bin_to_term(fun(X, Y) -> {X,Y} end), + ?line bad_bin_to_term(fun(X, Y, Z) -> {X,Y,Z} end), + ?line bad_bin_to_term(bit_sized_binary(term_to_binary({you,should,'not',see,this,term}))), + + %% Bad float. + ?line bad_bin_to_term(<<131,70,-1:64>>), + ok. + +bad_bin_to_term(BadBin) -> + {'EXIT',{badarg,_}} = (catch binary_to_term(BadBin)). + +%% Tests bad input to binary_to_term/1. + +bad_terms(suite) -> []; +bad_terms(Config) when is_list(Config) -> + ?line test_terms(fun corrupter/1). + +corrupter(Term) -> + ?line try + S = io_lib:format("About to corrupt: ~P", [Term,12]), + io:put_chars(S) + catch + error:badarg -> + io:format("About to corrupt: <<bit-level-binary:~p", + [bit_size(Term)]) + end, + ?line Bin = term_to_binary(Term), + ?line corrupter(Bin, size(Bin)-1), + ?line CompressedBin = term_to_binary(Term, [compressed]), + ?line corrupter(CompressedBin, size(CompressedBin)-1). + +corrupter(Bin, Pos) when Pos >= 0 -> + ?line {ShorterBin, _} = split_binary(Bin, Pos), + ?line catch binary_to_term(ShorterBin), %% emulator shouldn't crash + ?line MovedBin = list_to_binary([ShorterBin]), + ?line catch binary_to_term(MovedBin), %% emulator shouldn't crash + ?line corrupter(MovedBin, Pos-1); +corrupter(_Bin, _) -> + ok. + +more_bad_terms(suite) -> []; +more_bad_terms(Config) when is_list(Config) -> + ?line Data = ?config(data_dir, Config), + ?line BadFile = filename:join(Data, "bad_binary"), + ?line ok = io:format("File: ~s\n", [BadFile]), + ?line case file:read_file(BadFile) of + {ok,Bin} -> + ?line {'EXIT',{badarg,_}} = (catch binary_to_term(Bin)), + ok; + Other -> + ?line ?t:fail(Other) + end. + +otp_5484(Config) when is_list(Config) -> + ?line {'EXIT',_} = + (catch + binary_to_term( + <<131, + 104,2, %Tuple, 2 elements + 103, %Pid + 100,0,20,"wslin1427198@wslin14", + %% Obviously bad values follow. + 255,255,255,255, + 255,255,255,255, + 255, + 106>>)), + + ?line {'EXIT',_} = + (catch + binary_to_term( + <<131, + 104,2, %Tuple, 2 elements + 103, %Pid + 106, %[] instead of atom. + 0,0,0,17, + 0,0,0,135, + 2, + 106>>)), + + ?line {'EXIT',_} = + (catch + binary_to_term( + %% A old-type fun in a list containing a bad creator pid. + <<131,108,0,0,0,1,117,0,0,0,0,103,100,0,13,110,111,110,111,100,101,64,110,111,104,111,115,116,255,255,0,25,255,0,0,0,0,100,0,1,116,97,0,98,6,142,121,72,106>>)), + + ?line {'EXIT',_} = + (catch + binary_to_term( + %% A new-type fun in a list containing a bad creator pid. + %% + <<131, + 108,0,0,0,1, %List, 1 element + 112,0,0,0,66,0,52,216,81,158,148,250,237,109,185,9,208,60,202,156,244,218,0,0,0,0,0,0,0,0,100,0,1,116,97,0,98,6,142,121,72, + 103, %Pid. + 106, %[] instead of an atom. + 0,0,0,27,0,0,0,0,0,106>>)), + + ?line {'EXIT',_} = + (catch + binary_to_term( + %% A new-type fun in a list containing a bad module. + <<131, + 108,0,0,0,1, %List, 1 element + 112,0,0,0,70,0,224,90,4,101,48,28,110,228,153,48,239,169,232,77,108,145,0,0,0,0,0,0,0,2, + %%100,0,1,116, + 107,0,1,64, %String instead of atom (same length). + 97,0,98,6,64,82,230,103,100,0,13,110,111,110,111,100,101,64,110,111,104,111,115,116,0,0,0,48,0,0,0,0,0,97,42,97,7,106>>)), + + ?line {'EXIT',_} = + (catch + binary_to_term( + %% A new-type fun in a list containing a bad index. + <<131, + 108,0,0,0,1, %List, 1 element + 112,0,0,0,70,0,224,90,4,101,48,28,110,228,153,48,239,169,232,77,108,145,0,0,0,0,0,0,0,2, + 100,0,1,116, + %%97,0, %Integer: 0. + 104,0, %Tuple {} instead of integer. + 98,6,64,82,230,103,100,0,13,110,111,110,111,100,101,64,110,111,104,111,115,116,0,0,0,48,0,0,0,0,0,97,42,97,7,106>>)), + + ?line {'EXIT',_} = + (catch + binary_to_term( + %% A new-type fun in a list containing a bad unique value. + <<131, + 108,0,0,0,1, %List, 1 element + 112,0,0,0,70,0,224,90,4,101,48,28,110,228,153,48,239,169,232,77,108,145,0,0,0,0,0,0,0,2, + 100,0,1,116, + 97,0, %Integer: 0. + %%98,6,64,82,230, %Integer. + 100,0,2,64,65, %Atom instead of integer. + 103,100,0,13,110,111,110,111,100,101,64,110,111,104,111,115,116,0,0,0,48,0,0,0,0,0,97,42,97,7,106>>)), + + %% An absurdly large atom. + ?line {'EXIT',_} = + (catch binary_to_term(iolist_to_binary([<<131,100,65000:16>>| + lists:duplicate(65000, 42)]))), + + %% Longer than 255 characters. + ?line {'EXIT',_} = + (catch binary_to_term(iolist_to_binary([<<131,100,256:16>>| + lists:duplicate(256, 42)]))), + + %% OTP-7218. Thanks to Matthew Dempsky. Also make sure that we + %% cover the other error cases for external funs (EXPORT_EXT). + ?line {'EXIT',_} = + (catch binary_to_term( + <<131, + 113, %EXPORT_EXP + 97,13, %Integer: 13 + 97,13, %Integer: 13 + 97,13>>)), %Integer: 13 + ?line {'EXIT',_} = + (catch binary_to_term( + <<131, + 113, %EXPORT_EXP + 100,0,1,64, %Atom: '@' + 97,13, %Integer: 13 + 97,13>>)), %Integer: 13 + ?line {'EXIT',_} = + (catch binary_to_term( + <<131, + 113, %EXPORT_EXP + 100,0,1,64, %Atom: '@' + 100,0,1,64, %Atom: '@' + 106>>)), %NIL + ?line {'EXIT',_} = + (catch binary_to_term( + <<131, + 113, %EXPORT_EXP + 100,0,1,64, %Atom: '@' + 100,0,1,64, %Atom: '@' + 98,255,255,255,255>>)), %Integer: -1 + ?line {'EXIT',_} = + (catch binary_to_term( + <<131, + 113, %EXPORT_EXP + 100,0,1,64, %Atom: '@' + 100,0,1,64, %Atom: '@' + 113,97,13,97,13,97,13>>)), %fun 13:13/13 + + %% Bad funs. + ?line {'EXIT',_} = (catch binary_to_term(fake_fun(0, lists:seq(0, 256)))), + ok. + +fake_fun(Arity, Env0) -> + Uniq = erlang:md5([]), + Index = 0, + NumFree = length(Env0), + Mod = list_to_binary(?MODULE_STRING), + OldIndex = 0, + OldUniq = 16#123456, + <<131,Pid/binary>> = term_to_binary(self()), + Env1 = [term_to_binary(Term) || Term <- Env0], + Env = << <<Bin/binary>> || <<131,Bin/binary>> <- Env1 >>, + B = <<Arity,Uniq/binary,Index:32,NumFree:32, + $d,(byte_size(Mod)):16,Mod/binary, %Module. + $a,OldIndex:8, + $b,OldUniq:32, + Pid/binary,Env/binary>>, + <<131,$p,(byte_size(B)+4):32,B/binary>>. + + +%% More bad terms submitted by Matthias Lang. +otp_5933(Config) when is_list(Config) -> + ?line try_bad_lengths(<<131,$m>>), %binary + ?line try_bad_lengths(<<131,$n>>), %bignum + ?line try_bad_lengths(<<131,$o>>), %huge bignum + ok. + +try_bad_lengths(B) -> + try_bad_lengths(B, 16#FFFFFFFF). + +try_bad_lengths(B, L) when L > 16#FFFFFFF0 -> + Bin = <<B/binary,L:32>>, + io:format("~p\n", [Bin]), + {'EXIT',_} = (catch binary_to_term(Bin)), + try_bad_lengths(B, L-1); +try_bad_lengths(_, _) -> ok. + + +otp_6817(Config) when is_list(Config) -> + process_flag(min_heap_size, 20000), %Use the heap, not heap fragments. + + %% Floats are only validated when the heap fragment has been allocated. + BadFloat = <<131,99,53,46,48,$X,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,101,45,48,49,0,0,0,0,0>>, + ?line otp_6817_try_bin(BadFloat), + + %% {Binary,BadFloat}: When the error in float is discovered, a refc-binary + %% has been allocated and the list of refc-binaries goes through the + %% limbo area between the heap top and stack. + BinAndFloat = + <<131,104,2,109,0,0,1,0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20, + 21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45, + 46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70, + 71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95, + 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115, + 116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134, + 135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153, + 154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172, + 173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191, + 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210, + 211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229, + 230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248, + 249,250,251,252,253,254,255,99,51,46,49,52,$B,$l,$u,$r,$f,48,48,48,48,48,48, + 48,48,49,50,52,51,52,101,43,48,48,0,0,0,0,0>>, + ?line otp_6817_try_bin(BinAndFloat), + + %% {Fun,BadFloat} + FunAndFloat = + <<131,104,2,112,0,0,0,66,0,238,239,135,138,137,216,89,57,22,111,52,126,16,84, + 71,8,0,0,0,0,0,0,0,0,100,0,1,116,97,0,98,5,175,169,123,103,100,0,13,110,111, + 110,111,100,101,64,110,111,104,111,115,116,0,0,0,41,0,0,0,0,0,99,50,46,55,48, + $Y,57,57,57,57,57,57,57,57,57,57,57,57,57,54,52,52,55,101,43,48,48,0,0,0,0,0>>, + ?line otp_6817_try_bin(FunAndFloat), + + %% [ExternalPid|BadFloat] + ExtPidAndFloat = + <<131,108,0,0,0,1,103,100,0,13,107,97,108,108,101,64,115,116,114,105,100,101, + 114,0,0,0,36,0,0,0,0,2,99,48,46,$@,48,48,48,48,48,48,48,48,48,48,48,48,48,48, + 48,48,48,48,48,101,43,48,48,0,0,0,0,0>>, + ?line otp_6817_try_bin(ExtPidAndFloat), + ok. + +otp_6817_try_bin(Bin) -> + erlang:garbage_collect(), + + %% If the bug is present, the heap pointer will moved when the invalid term + %% is found and we will have a linked list passing through the limbo area + %% between the heap top and the stack pointer. + catch binary_to_term(Bin), + + %% If the bug is present, we will overwrite the pointers in the limbo area. + Filler = erlang:make_tuple(1024, 16#3FA), + id(Filler), + + %% Will crash if the bug is present. + erlang:garbage_collect(). + +otp_8117(doc) -> "Some bugs in binary_to_term when 32-bit integers are negative."; +otp_8117(suite) -> []; +otp_8117(Config) when is_list(Config) -> + [otp_8117_do(Op,-(1 bsl N)) || Op <- ['fun',list,tuple], + N <- lists:seq(0,31)], + ok. + +otp_8117_do('fun',Neg) -> + % Fun with negative num_free + FunBin = term_to_binary(fun() -> ok end), + ?line <<B1:27/binary,_NumFree:32,Rest/binary>> = FunBin, + ?line bad_bin_to_term(<<B1/binary,Neg:32,Rest/binary>>); +otp_8117_do(list,Neg) -> + %% List with negative length + ?line bad_bin_to_term(<<131,104,2,108,Neg:32,97,11,104,1,97,12,97,13,106,97,14>>); +otp_8117_do(tuple,Neg) -> + %% Tuple with negative arity + ?line bad_bin_to_term(<<131,104,2,105,Neg:32,97,11,97,12,97,13,97,14>>). + + +ordering(doc) -> "Tests ordering of binaries."; +ordering(suite) -> []; +ordering(Config) when is_list(Config) -> + B1 = list_to_binary([7,8,9]), + B2 = make_sub_binary([1,2,3,4]), + B3 = list_to_binary([1,2,3,5]), + Unaligned = make_unaligned_sub_binary(B2), + + %% From R8 binaries are compared as strings. + + ?line false = B1 == B2, + ?line false = B1 =:= B2, + ?line true = B1 /= B2, + ?line true = B1 =/= B2, + + ?line true = B1 > B2, + ?line true = B2 < B3, + ?line true = B2 =< B1, + ?line true = B2 =< B3, + + ?line true = B2 =:= Unaligned, + ?line true = B2 == Unaligned, + ?line true = Unaligned < B3, + ?line true = Unaligned =< B3, + + %% Binaries are greater than all other terms. + + ?line true = B1 > 0, + ?line true = B1 > 39827491247298471289473333333333333333333333333333, + ?line true = B1 > -3489274937438742190467869234328742398347, + ?line true = B1 > 3.14, + ?line true = B1 > [], + ?line true = B1 > [a], + ?line true = B1 > {a}, + ?line true = B1 > self(), + ?line true = B1 > make_ref(), + ?line true = B1 > xxx, + ?line true = B1 > fun() -> 1 end, + ?line true = B1 > fun erlang:send/2, + + ?line Path = ?config(priv_dir, Config), + ?line AFile = filename:join(Path, "vanilla_file"), + ?line Port = open_port(AFile, [out]), + ?line true = B1 > Port, + + ?line true = B1 >= 0, + ?line true = B1 >= 39827491247298471289473333333333333333333333333333, + ?line true = B1 >= -3489274937438742190467869234328742398347, + ?line true = B1 >= 3.14, + ?line true = B1 >= [], + ?line true = B1 >= [a], + ?line true = B1 >= {a}, + ?line true = B1 >= self(), + ?line true = B1 >= make_ref(), + ?line true = B1 >= xxx, + ?line true = B1 >= fun() -> 1 end, + ?line true = B1 >= fun erlang:send/2, + ?line true = B1 >= Port, + + ok. + +%% Test that comparisions between binaries with different alignment work. +unaligned_order(Config) when is_list(Config) -> + L = lists:seq(0, 7), + [test_unaligned_order(I, J) || I <- L, J <- L], + ok. + +test_unaligned_order(I, J) -> + Align = {I,J}, + io:format("~p ~p", [I,J]), + ?line true = test_unaligned_order_1('=:=', <<1,2,3,16#AA,16#7C,4,16#5F,5,16#5A>>, + <<1,2,3,16#AA,16#7C,4,16#5F,5,16#5A>>, + Align), + ?line false = test_unaligned_order_1('=/=', <<1,2,3>>, <<1,2,3>>, Align), + ?line true = test_unaligned_order_1('==', <<4,5,6>>, <<4,5,6>>, Align), + ?line false = test_unaligned_order_1('/=', <<1,2,3>>, <<1,2,3>>, Align), + + ?line true = test_unaligned_order_1('<', <<1,2>>, <<1,2,3>>, Align), + ?line true = test_unaligned_order_1('=<', <<1,2>>, <<1,2,3>>, Align), + ?line true = test_unaligned_order_1('=<', <<1,2,7,8>>, <<1,2,7,8>>, Align), + ok. + +test_unaligned_order_1(Op, A, B, {Aa,Ba}) -> + erlang:Op(unaligned_sub_bin(A, Aa), unaligned_sub_bin(B, Ba)). + +test_terms(Test_Func) -> + ?line Test_Func(atom), + ?line Test_Func(''), + ?line Test_Func('a'), + ?line Test_Func('ab'), + ?line Test_Func('abc'), + ?line Test_Func('abcd'), + ?line Test_Func('abcde'), + ?line Test_Func('abcdef'), + ?line Test_Func('abcdefg'), + ?line Test_Func('abcdefgh'), + + ?line Test_Func(fun() -> ok end), + X = id([a,{b,c},c]), + Y = id({x,y,z}), + Z = id(1 bsl 8*257), + ?line Test_Func(fun() -> X end), + ?line Test_Func(fun() -> {X,Y} end), + ?line Test_Func([fun() -> {X,Y,Z} end, + fun() -> {Z,X,Y} end, + fun() -> {Y,Z,X} end]), + + ?line Test_Func({trace_ts,{even_bigger,{some_data,fun() -> ok end}},{1,2,3}}), + ?line Test_Func({trace_ts,{even_bigger,{some_data,<<1,2,3,4,5,6,7,8,9,10>>}}, + {1,2,3}}), + + ?line Test_Func(1), + ?line Test_Func(42), + ?line Test_Func(-23), + ?line Test_Func(256), + ?line Test_Func(25555), + ?line Test_Func(-3333), + + ?line Test_Func(1.0), + + ?line Test_Func(183749783987483978498378478393874), + ?line Test_Func(-37894183749783987483978498378478393874), + Very_Big = very_big_num(), + ?line Test_Func(Very_Big), + ?line Test_Func(-Very_Big+1), + + ?line Test_Func([]), + ?line Test_Func("abcdef"), + ?line Test_Func([a, b, 1, 2]), + ?line Test_Func([a|b]), + + ?line Test_Func({}), + ?line Test_Func({1}), + ?line Test_Func({a, b}), + ?line Test_Func({a, b, c}), + ?line Test_Func(list_to_tuple(lists:seq(0, 255))), + ?line Test_Func(list_to_tuple(lists:seq(0, 256))), + + ?line Test_Func(make_ref()), + ?line Test_Func([make_ref(), make_ref()]), + + ?line Test_Func(make_port()), + + ?line Test_Func(make_pid()), + + ?line Test_Func(Bin0 = list_to_binary(lists:seq(0, 14))), + ?line Test_Func(Bin1 = list_to_binary(lists:seq(0, ?heap_binary_size))), + ?line Test_Func(Bin2 = list_to_binary(lists:seq(0, ?heap_binary_size+1))), + ?line Test_Func(Bin3 = list_to_binary(lists:seq(0, 255))), + + ?line Test_Func(make_unaligned_sub_binary(Bin0)), + ?line Test_Func(make_unaligned_sub_binary(Bin1)), + ?line Test_Func(make_unaligned_sub_binary(Bin2)), + ?line Test_Func(make_unaligned_sub_binary(Bin3)), + + ?line Test_Func(make_sub_binary(lists:seq(42, 43))), + ?line Test_Func(make_sub_binary([42,43,44])), + ?line Test_Func(make_sub_binary([42,43,44,45])), + ?line Test_Func(make_sub_binary([42,43,44,45,46])), + ?line Test_Func(make_sub_binary([42,43,44,45,46,47])), + ?line Test_Func(make_sub_binary([42,43,44,45,46,47,48])), + ?line Test_Func(make_sub_binary(lists:seq(42, 49))), + ?line Test_Func(make_sub_binary(lists:seq(0, 14))), + ?line Test_Func(make_sub_binary(lists:seq(0, ?heap_binary_size))), + ?line Test_Func(make_sub_binary(lists:seq(0, ?heap_binary_size+1))), + ?line Test_Func(make_sub_binary(lists:seq(0, 255))), + + ?line Test_Func(make_unaligned_sub_binary(lists:seq(42, 43))), + ?line Test_Func(make_unaligned_sub_binary([42,43,44])), + ?line Test_Func(make_unaligned_sub_binary([42,43,44,45])), + ?line Test_Func(make_unaligned_sub_binary([42,43,44,45,46])), + ?line Test_Func(make_unaligned_sub_binary([42,43,44,45,46,47])), + ?line Test_Func(make_unaligned_sub_binary([42,43,44,45,46,47,48])), + ?line Test_Func(make_unaligned_sub_binary(lists:seq(42, 49))), + ?line Test_Func(make_unaligned_sub_binary(lists:seq(0, 14))), + ?line Test_Func(make_unaligned_sub_binary(lists:seq(0, ?heap_binary_size))), + ?line Test_Func(make_unaligned_sub_binary(lists:seq(0, ?heap_binary_size+1))), + ?line Test_Func(make_unaligned_sub_binary(lists:seq(0, 255))), + + %% Bit level binaries. + ?line Test_Func(<<1:1>>), + ?line Test_Func(<<2:2>>), + ?line Test_Func(<<42:10>>), + ?line Test_Func(list_to_bitstring([<<5:6>>|lists:seq(0, 255)])), + + ?line Test_Func(F = fun(A) -> 42*A end), + ?line Test_Func(lists:duplicate(32, F)), + + ?line Test_Func(FF = fun binary_SUITE:all/1), + ?line Test_Func(lists:duplicate(32, FF)), + + ok. + +test_floats(Test_Func) -> + ?line Test_Func(5.5), + ?line Test_Func(-15.32), + ?line Test_Func(1.2435e25), + ?line Test_Func(1.2333e-20), + ?line Test_Func(199.0e+15), + ok. + +very_big_num() -> + very_big_num(33, 1). + +very_big_num(Left, Result) when Left > 0 -> + ?line very_big_num(Left-1, Result*256); +very_big_num(0, Result) -> + ?line Result. + +make_port() -> + ?line open_port({spawn, efile}, [eof]). + +make_pid() -> + ?line spawn_link(?MODULE, sleeper, []). + +sleeper() -> + ?line receive after infinity -> ok end. + + +gc_test(doc) -> "Test that binaries are garbage collected properly."; +gc_test(suite) -> []; +gc_test(Config) when is_list(Config) -> + case erlang:system_info(heap_type) of + private -> gc_test_1(); + hybrid -> {skip,"Hybrid heap"} + end. + +gc_test_1() -> + %% Note: This test is only relevant for REFC binaries. + %% Therefore, we take care that all binaries are REFC binaries. + B = list_to_binary(lists:seq(0, ?heap_binary_size)), + Self = self(), + F1 = fun() -> + gc(), + {binary,[]} = process_info(self(), binary), + Self ! {self(),done} + end, + F = fun() -> + receive go -> ok end, + {binary,[{_,65,1}]} = process_info(self(), binary), + gc(), + {B1,B2} = my_split_binary(B, 4), + gc(), + gc(), + {binary,L1} = process_info(self(), binary), + [Binfo1,Binfo2,Binfo3] = L1, + {_,65,3} = Binfo1 = Binfo2 = Binfo3, + 65 = size(B), + 4 = size(B1), + 61 = size(B2), + F1() + end, + gc(), + gc(), + 65 = size(B), + gc_test1(spawn_opt(erlang, apply, [F,[]], [link,{fullsweep_after,0}])). + +gc_test1(Pid) -> + gc(), + Pid ! go, + receive + {Pid,done} -> ok + after 10000 -> + ?line ?t:fail() + end. + +%% Like split binary, but returns REFC binaries. Only useful for gc_test/1. + +my_split_binary(B, Pos) -> + Self = self(), + Ref = make_ref(), + spawn(fun() -> Self ! {Ref,split_binary(B, Pos)} end), + receive + {Ref,Result} -> Result + end. + +gc() -> + erlang:garbage_collect(), + gc1(). +gc1() -> ok. + +bit_sized_binary_sizes(Config) when is_list(Config) -> + ?line [bsbs_1(A) || A <- lists:seq(0, 7)], + ok. + +bsbs_1(0) -> + BinSize = 32+8, + io:format("A: ~p BinSize: ~p", [0,BinSize]), + Bin = binary_to_term(<<131,$M,5:32,0,0,0,0,0,0>>), + BinSize = bit_size(Bin); +bsbs_1(A) -> + BinSize = 32+A, + io:format("A: ~p BinSize: ~p", [A,BinSize]), + Bin = binary_to_term(<<131,$M,5:32,A,0,0,0,0,0>>), + BinSize = bit_size(Bin). + +bitlevel_roundtrip(Config) when is_list(Config) -> + case ?t:is_release_available("r11b") of + true -> bitlevel_roundtrip_1(); + false -> {skip,"No R11B found"} + end. + +bitlevel_roundtrip_1() -> + Name = bitlevelroundtrip, + ?line N = list_to_atom(atom_to_list(Name) ++ "@" ++ hostname()), + ?line ?t:start_node(Name, slave, [{erl,[{release,"r11b"}]}]), + + ?line {<<128>>,1} = roundtrip(N, <<1:1>>), + ?line {<<64>>,2} = roundtrip(N, <<1:2>>), + ?line {<<16#E0>>,3} = roundtrip(N, <<7:3>>), + ?line {<<16#70>>,4} = roundtrip(N, <<7:4>>), + ?line {<<16#10>>,5} = roundtrip(N, <<2:5>>), + ?line {<<16#8>>,6} = roundtrip(N, <<2:6>>), + ?line {<<16#2>>,7} = roundtrip(N, <<1:7>>), + ?line {<<8,128>>,1} = roundtrip(N, <<8,1:1>>), + ?line {<<42,248>>,5} = roundtrip(N, <<42,31:5>>), + + ?line ?t:stop_node(N), + ok. + +roundtrip(Node, Term) -> + {badrpc,{'EXIT',Res}} = rpc:call(Node, erlang, exit, [Term]), + io:format("<<~p bits>> => ~w", [bit_size(Term),Res]), + Res. + +deep(Config) when is_list(Config) -> + ?line deep_roundtrip(lists:foldl(fun(E, A) -> + [E,A] + end, [], lists:seq(1, 1000000))), + ?line deep_roundtrip(lists:foldl(fun(E, A) -> + {E,A} + end, [], lists:seq(1, 1000000))), + ?line deep_roundtrip(lists:foldl(fun(E, A) -> + fun() -> {E,A} end + end, [], lists:seq(1, 1000000))), + ok. + +deep_roundtrip(T) -> + B = term_to_binary(T), + true = deep_eq(T, binary_to_term(B)). + +%% +%% FIXME: =:= runs out of stack. +%% +deep_eq([H1|T1], [H2|T2]) -> + deep_eq(H1, H2) andalso deep_eq(T1, T2); +deep_eq(T1, T2) when tuple_size(T1) =:= tuple_size(T2) -> + deep_eq_tup(T1, T2, tuple_size(T1)); +deep_eq(T1, T2) when is_function(T1), is_function(T2) -> + {uniq,U1} = erlang:fun_info(T1, uniq), + {index,I1} = erlang:fun_info(T1, index), + {arity,A1} = erlang:fun_info(T1, arity), + {env,E1} = erlang:fun_info(T1, env), + {uniq,U2} = erlang:fun_info(T2, uniq), + {index,I2} = erlang:fun_info(T2, index), + {arity,A2} = erlang:fun_info(T2, arity), + {env,E2} = erlang:fun_info(T2, env), + U1 =:= U2 andalso I1 =:= I2 andalso A1 =:= A2 andalso + deep_eq(E1, E2); +deep_eq(T1, T2) -> + T1 =:= T2. + +deep_eq_tup(_T1, _T2, 0) -> + true; +deep_eq_tup(T1, T2, N) -> + deep_eq(element(N, T1), element(N, T2)) andalso + deep_eq_tup(T1, T2, N-1). + +obsolete_funs(Config) when is_list(Config) -> + erts_debug:set_internal_state(available_internal_state, true), + + X = id({1,2,3}), + Y = id([a,b,c,d]), + Z = id({x,y,z}), + ?line obsolete_fun(fun() -> ok end), + ?line obsolete_fun(fun() -> X end), + ?line obsolete_fun(fun(A) -> {A,X} end), + ?line obsolete_fun(fun() -> {X,Y} end), + ?line obsolete_fun(fun() -> {X,Y,Z} end), + + ?line obsolete_fun(fun ?MODULE:all/1), + + erts_debug:set_internal_state(available_internal_state, false), + ok. + +obsolete_fun(Fun) -> + Tuple = case erlang:fun_info(Fun, type) of + {type,external} -> + {module,M} = erlang:fun_info(Fun, module), + {name,F} = erlang:fun_info(Fun, name), + {M,F}; + {type,local} -> + {module,M} = erlang:fun_info(Fun, module), + {index,I} = erlang:fun_info(Fun, index), + {uniq,U} = erlang:fun_info(Fun, uniq), + {env,E} = erlang:fun_info(Fun, env), + {'fun',M,I,U,list_to_tuple(E)} + end, + Tuple = no_fun_roundtrip(Fun). + +no_fun_roundtrip(Term) -> + binary_to_term(erts_debug:get_internal_state({term_to_binary_no_funs,Term})). + +%% Test non-standard encodings never generated by term_to_binary/1 +%% but recognized by binary_to_term/1. + +robustness(Config) when is_list(Config) -> + ?line [] = binary_to_term(<<131,107,0,0>>), %Empty string. + ?line [] = binary_to_term(<<131,108,0,0,0,0,106>>), %Zero-length list. + + %% {[],a} where [] is a zero-length list. + ?line {[],a} = binary_to_term(<<131,104,2,108,0,0,0,0,106,100,0,1,97>>), + + %% {42,a} where 42 is a zero-length list with 42 in the tail. + ?line {42,a} = binary_to_term(<<131,104,2,108,0,0,0,0,97,42,100,0,1,97>>), + + %% {{x,y},a} where {x,y} is a zero-length list with {x,y} in the tail. + ?line {{x,y},a} = binary_to_term(<<131,104,2,108,0,0,0,0, + 104,2,100,0,1,120,100,0,1, + 121,100,0,1,97>>), + + %% Bignums fitting in 32 bits. + ?line 16#7FFFFFFF = binary_to_term(<<131,98,127,255,255,255>>), + ?line -1 = binary_to_term(<<131,98,255,255,255,255>>), + + ok. + +%% OTP-8180: Test several terms that have been known to crash the emulator. +%% (Thanks to Scott Lystig Fritchie.) +otp_8180(Config) when is_list(Config) -> + ?line Data = ?config(data_dir, Config), + ?line Wc = filename:join(Data, "zzz.*"), + Files = filelib:wildcard(Wc), + [run_otp_8180(F) || F <- Files], + ok. + +run_otp_8180(Name) -> + io:format("~s", [Name]), + ?line {ok,Bins} = file:consult(Name), + [begin + io:format("~p\n", [Bin]), + ?line {'EXIT',{badarg,_}} = (catch binary_to_term(Bin)) + end || Bin <- Bins], + ok. + +%% Utilities. + +make_sub_binary(Bin) when is_binary(Bin) -> + {_,B} = split_binary(list_to_binary([0,1,3,Bin]), 3), + B; +make_sub_binary(List) -> + make_sub_binary(list_to_binary(List)). + +make_unaligned_sub_binary(Bin0) when is_binary(Bin0) -> + Bin1 = <<0:3,Bin0/binary,31:5>>, + Sz = size(Bin0), + <<0:3,Bin:Sz/binary,31:5>> = id(Bin1), + Bin; +make_unaligned_sub_binary(List) -> + make_unaligned_sub_binary(list_to_binary(List)). + +%% Add 1 bit to the size of the binary. +bit_sized_binary(Bin0) -> + Bin = <<Bin0/binary,1:1>>, + BitSize = bit_size(Bin), + BitSize = 8*size(Bin) + 1, + Bin. + +unaligned_sub_bin(Bin, 0) -> Bin; +unaligned_sub_bin(Bin0, Offs) -> + F = random:uniform(256), + Roffs = 8-Offs, + Bin1 = <<F:Offs,Bin0/binary,F:Roffs>>, + Sz = size(Bin0), + <<_:Offs,Bin:Sz/binary,_:Roffs>> = id(Bin1), + Bin. + +hostname() -> + from($@, atom_to_list(node())). + +from(H, [H | T]) -> T; +from(H, [_ | T]) -> from(H, T); +from(_, []) -> []. + +id(I) -> I. |