odeci / erlang / test / num_SUITE.erl

-module(num_SUITE).
-compile(export_all).
-include_lib("common_test/include/ct.hrl").

-define(d_precision, 100).
-define(d_precision_short, ?d_precision - 2).
-define(d_context, [{precision, ?d_precision}]).
-define(d_context_short, [{precision, ?d_precision_short}]).

% -----------------------------------------------------------------------------
% Function: suite() -> Info
% Info = [tuple()]
% -----------------------------------------------------------------------------
suite() ->
    [
     {require, length},
     {require, odeci_prog},
     {timetrap,{seconds,10}}
    ].

% -----------------------------------------------------------------------------
% Function: init_per_suite(Config0) ->
%               Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
% Config0 = Config1 = [tuple()]
% Reason = term()
% -----------------------------------------------------------------------------
init_per_suite(Config) ->
    application:start(crypto), %% for random numbers
    Config.

% -----------------------------------------------------------------------------
% Function: end_per_suite(Config0) -> void() | {save_config,Config1}
% Config0 = Config1 = [tuple()]
% -----------------------------------------------------------------------------
end_per_suite(_Config) ->
    ok.

% -----------------------------------------------------------------------------
% Function: init_per_group(GroupName, Config0) ->
%               Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
% GroupName = atom()
% Config0 = Config1 = [tuple()]
% Reason = term()
% -----------------------------------------------------------------------------
init_per_group(_GroupName, Config) ->
    Config.

% -----------------------------------------------------------------------------
% Function: end_per_group(GroupName, Config0) ->
%               void() | {save_config,Config1}
% GroupName = atom()
% Config0 = Config1 = [tuple()]
% -----------------------------------------------------------------------------
end_per_group(_GroupName, _Config) ->
    ok.

% -----------------------------------------------------------------------------
% Function: init_per_testcase(TestCase, Config0) ->
%               Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
% TestCase = atom()
% Config0 = Config1 = [tuple()]
% Reason = term()
% -----------------------------------------------------------------------------
init_per_testcase(_TestCase, Config) ->
    Config.

% -----------------------------------------------------------------------------
% Function: end_per_testcase(TestCase, Config0) ->
%               void() | {save_config,Config1} | {fail,Reason}
% TestCase = atom()
% Config0 = Config1 = [tuple()]
% Reason = term()
% -----------------------------------------------------------------------------
end_per_testcase(_TestCase, _Config) ->
    ok.

% -----------------------------------------------------------------------------
% Function: groups() -> [Group]
% Group = {GroupName,Properties,GroupsAndTestCases}
% GroupName = atom()
% Properties = [parallel | sequence | Shuffle | {RepeatType,N}]
% GroupsAndTestCases = [Group | {group,GroupName} | TestCase]
% TestCase = atom()
% Shuffle = shuffle | {shuffle,{integer(),integer(),integer()}}
% RepeatType = repeat | repeat_until_all_ok | repeat_until_all_fail |
%              repeat_until_any_ok | repeat_until_any_fail
% N = integer() | forever
% -----------------------------------------------------------------------------
groups() ->
    [].

% -----------------------------------------------------------------------------
% Function: all() -> GroupsAndTestCases | {skip,Reason}
% GroupsAndTestCases = [{group,GroupName} | TestCase]
% GroupName = atom()
% TestCase = atom()
% Reason = term()
% -----------------------------------------------------------------------------
all() ->
    [
     %% start
     test1
    ].

%%=============================================================================
%% Auxillary functions
%%=============================================================================
start_prog(Config) ->
    Prog = ct:get_config(odeci_prog),
    Base = proplists:get_value(data_dir, Config),
    Dir = filename:dirname(filename:dirname(filename:dirname(Base))),
    Fullname = filename:join([Dir, "priv", Prog]),
    application:load(odeci),
    application:set_env(odeci, port_command, Fullname),
    ok = application:start(odeci).

ops() ->
    [
     divide,
     subtract,
     add,
     multiply
     ].

random_choice(List) ->
    Len = length(List),
    N = crypto:rand_uniform(1, Len + 1),
    lists:nth(N, List).

do_test_loop(Len) ->
    Ops = ops(),
    Ignore = ct:get_config(ignore_errors),
    do_test_loop2(Len, Ops, Ignore).

do_test_loop2(0, _Ops, _Ignore) ->
    ok;
do_test_loop2(Len, Ops, Ignore) ->
    case do_one_operation(Ops) of
        {true, _Op, _I1, _I2, _O1, _O2} ->
            do_test_loop2(Len-1, Ops, Ignore);
        {false, _Op, _I1, _I2, _O1, _O2} when Ignore == true ->
            do_test_loop2(Len-1, Ops, Ignore);
        {false, Op, I1, I2, O1, O2} ->
            ct:pal("one op error, op=~p~n",
                   "i1=~p~ni2=~p~n",
                   "o1=~p~no2=~p~n",
                   [Op, I1, I2, O1, O2])
    end.

-spec do_one_operation(list()) -> {boolean(),
                                   atom(),
                                   tuple(), tuple(),
                                   tuple(), tuple()
                                  }.

do_one_operation(Ops) ->
    Cur = random_choice(Ops),
    D1 = gen_one_decimal(),
    D2 = gen_one_decimal(),
    {R1, R2} = do_op(Cur, D1, D2),
    Verbose = ct:get_config(verbose, 0),
    if Verbose > 2 ->
            ct:pal("do_one_operation,~n"
                   "cur op: ~p,~n"
                   "d1=~p~nd2=~p,~n"
                   "r1=~p~nr2=~p~n", [Cur, D1, D2, R1, R2]);
       true ->
            ok
    end,
    Res = compare(R1, R2),
    {Res, Cur, D1, D2, R1, R2}.

gen_one_decimal() ->
    Sign = gen_sign(),
    Exp = gen_exponent(),
    Mant = gen_mantissa(),
    {Sign, Mant, Exp}.

gen_mantissa() ->
    case ct:get_config(random_mantissa) of
        true ->
            Min = ct:get_config(mantissa_min),
            Max = ct:get_config(mantissa_max),
            crypto:rand_uniform(Min, Max + 1);
        _ ->
            ct:get_config(mantissa_min, 0)
    end.

gen_exponent() ->
    case ct:get_config(random_exponent) of
        true ->
            Min = ct:get_config(exponent_min),
            Max = ct:get_config(exponent_max),
            crypto:rand_uniform(Min, Max + 1);
        _ ->
            ct:get_config(exponent_min, 0)
    end.

gen_sign() ->
    case ct:get_config(random_sign) of
        true ->
            crypto:rand_uniform(0, 2);
        _ ->
            0
    end.

do_op(divide, D1, D2) ->
    Res1 = c_server:divide(D1, D2),
    Res2 = decimal:divide(D1, D2, ?d_context),
    {Res1, Res2};
do_op(subtract, D1, D2) ->
    Res1 = c_server:subtract(D1, D2),
    Res2 = decimal:subtract(D1, D2, ?d_context),
    {Res1, Res2};
do_op(add, D1, D2) ->
    Res1 = c_server:add(D1, D2),
    Res2 = decimal:add(D1, D2, ?d_context),
    {Res1, Res2};
do_op(multiply, D1, D2) ->
    Res1 = c_server:multiply(D1, D2),
    Res2 = decimal:multiply(D1, D2, ?d_context),
    {Res1, Res2}.

compare({ok, R1}, R1) ->
    Verbose = ct:get_config(verbose, 0),
    if Verbose > 3 ->
            ct:pal("compare equal match~nr1=~p~n", [R1]);
       true ->
            ok
    end,
    true;
compare({ok, R1}, R2) ->
    case decimal:compare(R1, R2, ?d_context) of
        0 ->
            Verbose = ct:get_config(verbose, 0),
            if Verbose > 3 ->
                    ct:pal("compare equal~nr1=~p~nr2=~p~n", [R1, R2]);
               true ->
                    ok
            end,
            true;
        _ ->
            compare2(R1, R2)
    end;
compare(R1, R2) ->
    ct:pal("compare different, not even close~n~p~n~p~n", [R1, R2]),
    false.

%% this is necessary for divide only
compare2({_S1, _M1, E1} = R1, {_S2, _M2, E2} = R2) ->
    {_Ds, Dm, De} = decimal:subtract(R1, R2),
    Min_exp = min(E1, E2),
    if (Dm =< 1) and (De =< Min_exp) ->
            Verbose = ct:get_config(verbose, 0),
            if Verbose > 3 ->
                    ct:pal("compare almost equal~nr1=~p~nr2=~p~n", [R1, R2]);
               true ->
                    ok
            end,
            true;
       true ->
            ct:pal("compare different~n~p~n~p~n", [R1, R2]),
            false
    end.

% -----------------------------------------------------------------------------
% Function: TestCase(Config0) ->
%               ok | exit() | {skip,Reason} | {comment,Comment} |
%               {save_config,Config1} | {skip_and_save,Reason,Config1}
% Config0 = Config1 = [tuple()]
% Reason = term()
% Comment = term()
% -----------------------------------------------------------------------------

ping(Config) ->
    Node = ct:get_config(odeci_node),
    pong = net_adm:ping(Node),
    pong = rpc:call(Node, c_server, ping, []).

start(Config) ->
    start_prog(Config).

test1(Config) ->
    start_prog(Config),
    Warmup = ct:get_config(warm_up_time, 1000),
    timer:sleep(Warmup),
    Len = ct:get_config(length, 1),
    ct:timetrap(round(Len) * 10 + 10000),
    do_test_loop(Len).
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.