%% %% %CopyrightBegin% %% %% Copyright Ericsson AB 2000-2010. 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(bs_construct_SUITE). -export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2,init_per_testcase/2,end_per_testcase/2,init_per_suite/1,end_per_suite/1, test1/1, test2/1, test3/1, test4/1, test5/1, testf/1, not_used/1, in_guard/1, coerce_to_float/1]). -include_lib("test_server/include/test_server.hrl"). suite() -> [{suite_callbacks,[ts_install_scb]}]. all() -> [cases()]. groups() -> []. init_per_group(_GroupName, Config) -> Config. end_per_group(_GroupName, Config) -> Config. cases() -> [test1, test2, test3, test4, test5, testf, not_used, in_guard, coerce_to_float]. init_per_testcase(_Case, Config) -> test_lib:interpret(?MODULE), Dog = test_server:timetrap(?t:minutes(1)), [{watchdog,Dog}|Config]. end_per_testcase(_Case, Config) -> Dog = ?config(watchdog, Config), ?t:timetrap_cancel(Dog), ok. init_per_suite(Config) when is_list(Config) -> ?line test_lib:interpret(?MODULE), ?line true = lists:member(?MODULE, int:interpreted()), ok. end_per_suite(Config) when is_list(Config) -> ok. big(1) -> 57285702734876389752897683. i(X) -> X. r(L) -> lists:reverse(L). -define(T(B, L), {B, ??B, L}). -define(N(B), {B, ??B, unknown}). -define(FAIL(Expr), ?line {'EXIT',{badarg,_}} = (catch Expr)). l(I_13, I_big1) -> [ ?T(<<-43>>, [256-43]), ?T(<<56>>, [56]), ?T(<<1,2>>, [1, 2]), ?T(<<4:4, 7:4>>, [4*16+7]), ?T(<<777:16/big>>, [3, 9]), ?T(<<777:16/little>>, [9, 3]), ?T(<<0.0:32/float>>, [0,0,0,0]), ?T(<<0.125:32/float>>, [62,0,0,0]), ?T(<<0.125:32/little-float>>, [0,0,0,62]), ?T(<>, [138, 99, 0, 147]), ?T(<<57285702734876389752897684:32>>, [138, 99, 0, 148]), ?T(<>, r([138, 99, 0, 147])), ?T(<<-1:17/unit:8>>, lists:duplicate(17, 255)), ?T(<>, [13]), ?T(<<4:8/unit:2,5:2/unit:8>>, [0, 4, 0, 5]), ?T(<<1:1, 0:6, 1:1>>, [129]), ?T(<<1:1/little, 0:6/little, 1:1/little>>, [129]), ?T(<<<<1,2>>/binary>>, [1, 2]), ?T(<<<<1,2>>:1/binary>>, [1]), ?T(<<4,3,<<1,2>>:1/binary>>, [4,3,1]), ?T(<<(256*45+47)>>, [47]), ?T(<<57:0>>, []), ?T(<<"apa">>, "apa"), ?T(<<1:3,"string",9:5>>, [46,110,142,77,45,204,233]), ?T(<<>>, []), ?T(<<37.98:64/native-float>>, native_3798()), ?T(<<32978297842987249827298387697777669766334937:128/native-integer>>, native_bignum()) ]. native_3798() -> case <<1:16/native>> of <<0,1>> -> [64,66,253,112,163,215,10,61]; <<1,0>> -> [61,10,215,163,112,253,66,64] end. native_bignum() -> case <<1:16/native>> of <<0,1>> -> [129,205,18,177,1,213,170,101,39,231,109,128,176,11,73,217]; <<1,0>> -> [217,73,11,176,128,109,231,39,101,170,213,1,177,18,205,129] end. evaluate(Str, Vars) -> {ok,Tokens,_} = erl_scan:string(Str ++ " . "), {ok, [Expr]} = erl_parse:parse_exprs(Tokens), case erl_eval:expr(Expr, Vars) of {value, Result, _} -> Result end. eval_list([], _Vars) -> []; eval_list([{C_bin, Str, Bytes} | Rest], Vars) -> case catch evaluate(Str, Vars) of {'EXIT', Error} -> io:format("Evaluation error: ~p, ~p, ~p~n", [Str, Vars, Error]), exit(Error); E_bin -> [{C_bin, E_bin, Str, Bytes} | eval_list(Rest, Vars)] end. one_test({C_bin, E_bin, Str, Bytes}) when list(Bytes) -> io:format(" ~s, ~p~n", [Str, Bytes]), Bin = list_to_binary(Bytes), if C_bin == Bin -> ok; true -> io:format("ERROR: Compiled: ~p. Expected ~p. Got ~p.~n", [Str, Bytes, binary_to_list(C_bin)]), test_server:fail(comp) end, if E_bin == Bin -> ok; true -> io:format("ERROR: Interpreted: ~p. Expected ~p. Got ~p.~n", [Str, Bytes, binary_to_list(E_bin)]), test_server:fail(comp) end; one_test({C_bin, E_bin, Str, Result}) -> io:format(" ~s ~p~n", [Str, C_bin]), if C_bin == E_bin -> ok; true -> Arbitrary = case Result of unknown -> size(C_bin); _ -> Result end, case equal_lists(binary_to_list(C_bin), binary_to_list(E_bin), Arbitrary) of false -> io:format("ERROR: Compiled not equal to interpreted:" "~n ~p, ~p.~n", [binary_to_list(C_bin), binary_to_list(E_bin)]), test_server:fail(comp); 0 -> ok; %% For situations where the final bits may not matter, like %% for floats: N when integer(N) -> io:format("Info: compiled and interpreted differ in the" " last bytes:~n ~p, ~p.~n", [binary_to_list(C_bin), binary_to_list(E_bin)]), ok end end. equal_lists([], [], _) -> 0; equal_lists([], _, _) -> false; equal_lists(_, [], _) -> false; equal_lists([A|AR], [A|BR], R) -> equal_lists(AR, BR, R); equal_lists(A, B, R) -> if length(A) /= length(B) -> false; length(A) =< R -> R; true -> false end. %%% Simple working cases test1(suite) -> []; test1(Config) when list(Config) -> ?line I_13 = i(13), ?line I_big1 = big(1), ?line Vars = [{'I_13', I_13}, {'I_big1', I_big1}], ?line lists:foreach(fun one_test/1, eval_list(l(I_13, I_big1), Vars)). %%% Misc %%% <> comp(N, A, S) -> M1 = (1 bsl S) - 1, M2 = (1 bsl (N-S)) - 1, [((A band M1) bsl (N-S)) bor (A band M2)]. gen(N, S, A) -> [?T(<>, comp(N, A, S))]. gen_l(N, S, A) -> [?T(<>, comp(N, A, S))]. test2(suite) -> []; test2(Config) when list(Config) -> ?line test2(0, 8, 2#10101010101010101), ?line test2(0, 8, 2#1111111111). test2(End, End, _) -> ok; test2(I, End, A) -> test2(I, A), test2(I+1, End, A). test2(S, A) -> N = 8, Vars = [{'A',A}, {'N',N}, {'S',S}], io:format("Vars: ~p\n", [Vars]), lists:foreach(fun one_test/1, eval_list(gen(N, S, A), Vars)), lists:foreach(fun one_test/1, eval_list(gen_l(N, S, A), Vars)). %%% Tests without facit t3() -> [?N(<<4711:13, 9876:13, 3:6>>), ?N(<<4.57:64/float>>), ?N(<<4.57:32/float>>), ?N(<<>>) ]. test3(suite) -> []; test3(Config) when list(Config) -> ?line Vars = [], ?line lists:foreach(fun one_test/1, eval_list(t3(), Vars)). gen_u(N, S, A) -> [?N(<>)]. gen_u_l(N, S, A) -> [?N(<>)]. test4(suite) -> []; test4(Config) when list(Config) -> ?line test4(0, 16, 2#10101010101010101), ?line test4(0, 16, 2#1111111111). test4(End, End, _) -> ok; test4(I, End, A) -> test4(I, A), test4(I+1, End, A). test4(S, A) -> N = 16, Vars = [{'A', A}, {'N', 16}, {'S', S}], lists:foreach(fun one_test/1, eval_list(gen_u(N, S, A), Vars)), lists:foreach(fun one_test/1, eval_list(gen_u_l(N, S, A), Vars)). gen_b(N, S, A) -> [?T(<>, binary_to_list(<>))]. test5(suite) -> []; test5(doc) -> ["OTP-3995"]; test5(Config) when list(Config) -> ?line test5(0, 8, <<73>>), ?line test5(0, 8, <<68>>). test5(End, End, _) -> ok; test5(I, End, A) -> test5(I, A), test5(I+1, End, A). test5(S, A) -> N = 8, Vars = [{'A', A}, {'N', 8}, {'S', S}], lists:foreach(fun one_test/1, eval_list(gen_b(N, S, A), Vars)). %%% Failure cases testf(suite) -> []; testf(Config) when list(Config) -> ?FAIL(<<3.14>>), ?FAIL(<<<<1,2>>>>), ?FAIL(<<2.71/binary>>), ?FAIL(<<24334/binary>>), ?FAIL(<<24334344294788947129487129487219847/binary>>), ?FAIL(<<<<1,2,3>>/float>>), %% Negative field widths. testf_1(-8, <<1,2,3,4,5>>), ?FAIL(<<42:(-16)>>), ?FAIL(<<3.14:(-8)/float>>), ?FAIL(<<<<23,56,0,2>>:(-16)/binary>>), ?FAIL(<<<<23,56,0,2>>:(2.5)/binary>>), ?FAIL(<<<<23,56,0,2>>:(anka)>>), ok. testf_1(W, B) -> ?FAIL(<<42:W>>), ?FAIL(<<3.14:W/float>>), ?FAIL(<>). not_used(doc) -> "Test that constructed binaries that are not used will still give an exception."; not_used(Config) when is_list(Config) -> ?line ok = not_used1(3, <<"dum">>), ?line ?FAIL(not_used1(3, "dum")), ?line ?FAIL(not_used2(444, -2)), ?line ?FAIL(not_used2(444, anka)), ?line ?FAIL(not_used3(444)), ok. not_used1(I, BinString) -> <>, ok. not_used2(I, Sz) -> <>, ok. not_used3(I) -> <>, ok. in_guard(Config) when list(Config) -> ?line 1 = in_guard(<<16#74ad:16>>, 16#e95, 5), ?line 2 = in_guard(<<16#3A,16#F7,"hello">>, 16#3AF7, <<"hello">>), ?line 3 = in_guard(<<16#FBCD:14,3.1415/float,3:2>>, 16#FBCD, 3.1415), nope = in_guard(<<1>>, 42, b), nope = in_guard(<<1>>, a, b), nope = in_guard(<<1,2>>, 1, 1), nope = in_guard(<<4,5>>, 1, 2.71), nope = in_guard(<<4,5>>, 1, <<12,13>>), ok. in_guard(Bin, A, B) when <> == Bin -> 1; in_guard(Bin, A, B) when <> == Bin -> 2; in_guard(Bin, A, B) when <> == Bin -> 3; in_guard(Bin, A, B) when {a,b,<>} == Bin -> cant_happen; in_guard(_, _, _) -> nope. -define(COF(Int0), ?line (fun(Int) -> true = <> =:= <<(float(Int)):32/float>>, true = <> =:= <<(float(Int)):64/float>> end)(nonliteral(Int0)), ?line true = <> =:= <<(float(Int0)):32/float>>, ?line true = <> =:= <<(float(Int0)):64/float>>). -define(COF64(Int0), ?line (fun(Int) -> true = <> =:= <<(float(Int)):64/float>> end)(nonliteral(Int0)), ?line true = <> =:= <<(float(Int0)):64/float>>). nonliteral(X) -> X. coerce_to_float(Config) when list(Config) -> ?COF(0), ?COF(-1), ?COF(1), ?COF(42), ?COF(255), ?COF(-255), ?COF(38474), ?COF(387498738948729893849444444443), ?COF(-37489378937773899999999999999993), ?COF64(298748888888888888888888888883478264866528467367364766666666666666663), ?COF64(-367546729879999999999947826486652846736736476555566666663), ok.