aboutsummaryrefslogblamecommitdiffstats
path: root/erts/emulator/test/op_SUITE.erl
blob: 08655d32a5740e90a05bcbb60c658b5ae73ed286 (plain) (tree)
1
2
3
4
5


                   
                                                        
   










                                                                           





                  
                                           
 
                        
                                                                              



                                    

                                 
                               
 
         

                                                  
 


                                                                            









                                                           
 
                                                



                                                                     
                                  

                                                         
                                           
                                     

                                                             

                                                         
                                                              







                                                                            



                                                   
                                                         
                          
 
                   





                                                                         





                                                    


                       

                



                       
                   



                         
                  










                                               









                                                               

                              






                                                              
 


                            
                           






                                                         


                          

                                    











                                                 




                                     
                                     





                                                   
 





















                                                               


                                                                    
        
 




                                                   
 
 
                                                               





                                                        
                                  
                                                           
                   
 
                                                                       





                                                                 
                   

                 



                                                                   
                                                    
 
                                   














                                                                       

                                                         
                                                                             


                                                   
                                                   












                                                          






















                                             




                                         
        
 




                                          
                                                                
























                                                                              

                                    








                                                              
 

                               



                                           
 


                                  
 













                                                         
%%
%% %CopyrightBegin%
%% 
%% Copyright Ericsson AB 1999-2016. 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(op_SUITE).

-include_lib("common_test/include/ct.hrl").

-export([all/0, suite/0,
         bsl_bsr/1,logical/1,t_not/1,relop_simple/1,relop/1,complex_relop/1]).

-export([]).
-import(lists, [foldl/3,flatmap/2]).

suite() ->
    [{ct_hooks,[ts_install_cth]},
     {timetrap, {minutes, 5}}].

all() -> 
    [bsl_bsr, logical, t_not, relop_simple, relop,
     complex_relop].

%% Test the bsl and bsr operators.
bsl_bsr(Config) when is_list(Config) ->
    Vs = [unvalue(V) || V <- [-16#8000009-2,-1,0,1,2,73,16#8000000,bad,[]]],
    %% Try to use less memory by splitting the cases

    Cases1 = [{Op,X,Y} || Op <- ['bsl'], X <- Vs, Y <- Vs],
    N1 = length(Cases1),
    run_test_module(Cases1, false),

    Cases2 = [{Op,X,Y} || Op <- ['bsr'], X <- Vs, Y <- Vs],
    N2 = length(Cases2),
    run_test_module(Cases2, false),
    {comment,integer_to_list(N1 + N2) ++ " cases"}.

%% Test the logical operators and internal BIFs.
logical(Config) when is_list(Config) ->
    Vs0 = [true,false,bad],
    Vs = [unvalue(V) || V <- Vs0],
    Cases = [{Op,X,Y} || Op <- ['and','or','xor'], X <- Vs, Y <- Vs],
    run_test_module(Cases, false),
    {comment,integer_to_list(length(Cases)) ++ " cases"}.

%% Test the not operator and internal BIFs.
t_not(Config) when is_list(Config) ->
    Cases = [{'not',unvalue(V)} || V <- [true,false,42,bad]],
    run_test_module(Cases, false),
    {comment,integer_to_list(length(Cases)) ++ " cases"}.

%% Test that simlpe relations between relation operators hold.
relop_simple(Config) when is_list(Config) ->
    Big1 = 19738924729729787487784874,
    Big2 = 38374938373887374983978484,
    F1 = float(Big1),
    F2 = float(Big2),
    T1 = erlang:make_tuple(3,87),
    T2 = erlang:make_tuple(3,87),
    Terms = [-F2,Big2,-F1,-Big1,-33,-33.0,0,0.0,42,42.0,Big1,F1,Big2,F2,a,b,
             {T1,a},{T2,b},[T1,Big1],[T2,Big2]],

    Combos = [{V1,V2} || V1 <- Terms, V2 <- Terms],

    lists:foreach(fun({A,B}) -> relop_simple_do(A,B) end,
                  Combos),

    repeat(fun() ->
                   Size = rand:uniform(100),
                   Rnd1 = make_rand_term(Size),
                   {Rnd2,0} = clone_and_mutate(Rnd1, rand:uniform(Size)),
                   relop_simple_do(Rnd1,Rnd2)
           end,
           1000),
    ok.

relop_simple_do(V1,V2) ->
    %%io:format("compare ~p\n   and  ~p\n",[V1,V2]),

    L = V1 < V2,
    L = not (V1 >= V2),
    L = V2 > V1,
    L = not (V2 =< V1),

    G = V1 > V2,
    G = not (V1 =< V2),
    G = V2 < V1,
    G = not (V2 >= V1),

    ID = V1 =:= V2,
    ID = V2 =:= V1,
    ID = not (V1 =/= V2),
    ID = not (V2 =/= V1),

    EQ = V1 == V2,
    EQ = V2 == V1,
    EQ = not (V1 /= V2),
    EQ = not (V2 /= V1),

    case {L, EQ, ID, G, cmp_emu(V1,V2)} of
        { true, false, false, false, -1} -> ok;
        {false, true,  false, false,  0} -> ok;
        {false, true,   true, false,  0} -> ok;
        {false, false, false, true,  +1} -> ok
    end.

%% Emulate internal "cmp"
cmp_emu(A,B) when is_tuple(A), is_tuple(B) ->
    SA = size(A),
    SB = size(B),
    if SA =:= SB -> cmp_emu(tuple_to_list(A),tuple_to_list(B));
       SA > SB -> +1;
       SA < SB -> -1
    end;
cmp_emu([A|TA],[B|TB]) ->
    case cmp_emu(A,B) of
        0   -> cmp_emu(TA,TB);
        CMP -> CMP
    end;
cmp_emu(A,B) ->
    %% We cheat and use real "cmp" for the primitive types.
    if A < B -> -1;
       A > B -> +1;
       true -> 0
    end.					              

make_rand_term(1) ->
    make_rand_term_single();
make_rand_term(Arity) ->
    case rand:uniform(3) of
        1 ->
            make_rand_list(Arity);
        2 ->
            list_to_tuple(make_rand_list(Arity));
        3 ->
            {Car,Rest} = make_rand_term_rand_size(Arity),
            [Car|make_rand_term(Rest)]
    end.

make_rand_term_single() ->
    Range = 1 bsl rand:uniform(200),
    case rand:uniform(12) of
        1 -> random;
        2 -> uniform;
        3 -> rand:uniform(Range) - (Range div 2);
        4 -> Range * (rand:uniform() - 0.5);
        5 -> 0;
        6 -> 0.0;
        7 -> make_ref();
        8 -> self();
        9 -> term_to_binary(rand:uniform(Range));
        10 -> fun(X) -> X*Range end; 
        11 -> fun(X) -> X/Range end;
        12 -> []
    end.	    

make_rand_term_rand_size(1) ->
    {make_rand_term(1), 0};
make_rand_term_rand_size(MaxArity) ->
    Arity = rand:uniform(MaxArity-1),
    {make_rand_term(Arity), MaxArity-Arity}.

make_rand_list(0) -> [];
make_rand_list(Arity) ->
    {Term, Rest} = make_rand_term_rand_size(Arity),
    [Term | make_rand_list(Rest)].


clone_and_mutate(Term, 0) ->
    {clone(Term), 0};
clone_and_mutate(_Term, 1) ->
    {Mutation, _} = make_rand_term_rand_size(10), % MUTATE!
    {Mutation, 0};
clone_and_mutate(Term, Cnt) when is_tuple(Term) ->
    {Clone,NewCnt} = clone_and_mutate(tuple_to_list(Term),Cnt),
    {my_list_to_tuple(Clone), NewCnt};
clone_and_mutate([Term|Tail], Cnt) ->
    {Car,Cnt1} = clone_and_mutate(Term,Cnt),
    {Cdr,Cnt2} = clone_and_mutate(Tail,Cnt1),
    {[Car | Cdr], Cnt2};
clone_and_mutate(Term, Cnt) ->
    {clone(Term), Cnt-1}.

clone(Term) ->
    binary_to_term(term_to_binary(Term)).

my_list_to_tuple(List) ->
    try list_to_tuple(List)
    catch
        error:badarg -> 
            %%io:format("my_list_to_tuple got badarg exception.\n"),
            list_to_tuple(purify_list(List))
    end.

purify_list(List) ->
    lists:reverse(purify_list(List, [])).
purify_list([], Acc) -> Acc;
purify_list([H|T], Acc) -> purify_list(T, [H|Acc]);
purify_list(Other, Acc) -> [Other|Acc].


%% Test the relational operators and internal BIFs on literals.
relop(Config) when is_list(Config) ->
    Big1 = -38374938373887374983978484,
    Big2 = 19738924729729787487784874,
    F1 = float(Big1),
    F2 = float(Big2),
    Vs0 = [a,b,-33,-33.0,0,0.0,42,42.0,Big1,Big2,F1,F2],
    Vs = [unvalue(V) || V <- Vs0],
    Ops = ['==', '/=', '=:=', '=/=', '<', '=<', '>', '>='],
    binop(Ops, Vs).

%% Test the relational operators and internal BIFs on lists and tuples.
complex_relop(Config) when is_list(Config) ->
    Big = 99678557475484872464269855544643333,
    Float = float(Big),
    Vs0 = [an_atom,42.0,42,Big,Float],
    Vs = flatmap(fun(X) -> [unvalue({X}),unvalue([X])] end, Vs0),
    Ops = ['==', '/=', '=:=', '=/=', '<', '=<', '>', '>='],
    binop(Ops, Vs).

binop(Ops, Vs) ->
    Run = fun(Op, N) -> Cases = [{Op,V1,V2} || V1 <- Vs, V2 <- Vs],
                        run_test_module(Cases, true),
                        N + length(Cases) end,
    NumCases = foldl(Run, 0, Ops),
    {comment,integer_to_list(NumCases) ++ " cases"}.

run_test_module(Cases, GuardsOk) ->
    Es = [expr(C) || C <- Cases],
    Ok = unvalue(ok),
    Gts = case GuardsOk of
              true ->
                  Ges = [guard_expr(C) || C <- Cases],
                  lists:foldr(fun guard_test/2, [Ok], Ges);
              false ->
                  [Ok]
          end,
    Fun1 = make_function(guard_tests, Gts),
    Bts = lists:foldr(fun body_test/2, [Ok], Es),
    Fun2 = make_function(body_tests, Bts),
    Bbts = lists:foldr(fun internal_bif/2, [Ok], Es),
    Fun3 = make_function(bif_tests, Bbts),
    Id = {function,1,id,1,[{clause,1,[{var,1,'I'}],[],[{var,1,'I'}]}]},
    Module0 = make_module(op_tests, [Fun1,Fun2,Fun3,Id]),
    Module = erl_parse:new_anno(Module0),
    lists:foreach(fun(F) -> io:put_chars([erl_pp:form(F),"\n"]) end, Module),

    %% Compile, load, and run the generated module.

    Native = case test_server:is_native(?MODULE) of
                 true -> [native];
                 false -> []
             end,
    {ok,Mod,Code1} = compile:forms(Module, [time|Native]),
    code:delete(Mod),
    code:purge(Mod),
    {module,Mod} = code:load_binary(Mod, Mod, Code1),
    run_function(Mod, guard_tests),
    run_function(Mod, body_tests),
    run_function(Mod, bif_tests),

    true = code:delete(Mod),
    code:purge(Mod),

    ok.

expr({Op,X}) ->
    E = {op,1,Op,{call,1,{atom,1,id},[X]}},
    Res = eval([{op,1,Op,X}]),
    {E,{Op,X},Res};
expr({Op,X,Y}) ->
    E = {op,1,Op,{call,1,{atom,1,id},[X]},Y},
    Res = eval([{op,1,Op,X,Y}]),
    {E,{Op,value(X),value(Y)},Res}.

guard_expr({Op,X}) ->
    E = {op,1,Op,X},
    Res = eval([E]),
    {E,{Op,X},Res};
guard_expr({Op,X,Y}) ->
    E = {op,1,Op,X,Y},
    Res = eval([E]),
    {E,{Op,value(X),value(Y)},Res}.

run_function(Mod, Name) ->
    case catch Mod:Name() of
        {'EXIT',Reason} ->
            io:format("~p", [get(last)]),
            ct:fail({'EXIT',Reason});
        _Other ->
            ok
    end.

guard_test({E,Expr,Res}, Tail) ->
    True = unvalue(true),
    [save_term(Expr),
     {match,1,unvalue(Res),
      {'if',1,[{clause,1,[],[[E]],[True]},
               {clause,1,[],[[True]],[unvalue(false)]}]}}|Tail].

body_test({E,Expr,{'EXIT',_}}, Tail) ->
    [save_term(Expr),
     {match,1,{tuple,1,[unvalue('EXIT'), {var,1,'_'}]},
      {'catch',1,E}}|Tail];
body_test({E,Expr,Res}, Tail) ->
    [save_term(Expr),
     {match,1,unvalue(Res),E}|Tail].

internal_bif({{op,_,Op,X},Expr,Res}, Tail) ->
    internal_bif(Op, [X], Expr, Res, Tail);
internal_bif({{op,_,Op,X,Y},Expr,Res}, Tail) ->
    internal_bif(Op, [X,Y], Expr, Res, Tail).

internal_bif(Op, Args, Expr, {'EXIT',_}, Tail) ->
    [save_term(Expr),
     {match,1,{tuple,1,[unvalue('EXIT'), {var,1,'_'}]},
      {'catch',1,{call,1,{remote,1,{atom,1,erlang},unvalue(Op)},Args}}}|Tail];
internal_bif(Op, Args, Expr, Res, Tail) ->
    [save_term(Expr),
     {match,1,unvalue(Res),
      {call,1,{remote,1,{atom,1,erlang},unvalue(Op)},Args}}|Tail].

save_term(Term) ->
    {call,1,
     {atom,1,put},
     [{atom,1,last},unvalue(Term)]}.

make_module(Name, Funcs) ->
    [{attribute,1,module,Name},
     {attribute,0,compile,export_all},
     {attribute,0,compile,[{hipe,[{regalloc,linear_scan}]}]} |
     Funcs ++ [{eof,0}]].

make_function(Name, Body) ->
    {function,1,Name,0,[{clause,1,[],[],Body}]}.

eval(E0) ->
    E = erl_parse:new_anno(E0),
    case catch erl_eval:exprs(E, []) of
        {'EXIT',Reason} -> {'EXIT',Reason};
        {value,Val,_Bs} -> Val
    end.

unvalue(V) ->
    Abstr = erl_parse:abstract(V),
    erl_parse:anno_to_term(Abstr).

value({nil,_}) -> [];
value({integer,_,X}) -> X;
value({string,_,X}) -> X;
value({float,_,X})   -> X;
value({atom,_,X})    -> X;
value({tuple,_,Es}) ->
    list_to_tuple(lists:map(fun(X) -> value(X) end, Es));
value({cons,_,H,T}) ->
    [value(H) | value(T)].

repeat(_, 0) -> ok;
repeat(Fun, N) ->
    Fun(),
    repeat(Fun, N-1).