%%
%% %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%
%%
%%
%%-----------------------------------------------------------------
%% 
%% Description:
%% Test suite for the CDR basic type encode/decode functions
%%
%%-----------------------------------------------------------------
-module(cdrlib_SUITE).

-include("test_server.hrl").

-define(default_timeout, ?t:minutes(3)).

%%-----------------------------------------------------------------
%% External exports
%%-----------------------------------------------------------------
-export([all/1]).

%%-----------------------------------------------------------------
%% Internal exports
%%-----------------------------------------------------------------
-export([]).
-compile(export_all).

%%-----------------------------------------------------------------
%% Func: all/1
%% Args: 
%% Returns: 
%%-----------------------------------------------------------------
all(doc) -> ["Description", "more description"];
all(suite) -> 
    [short, ushort, long, ulong, longlong, ulonglong, boolean, character, octet,
     float, double, enum].

%%-----------------------------------------------------------------
%% Init and cleanup functions.
%%-----------------------------------------------------------------

init_per_testcase(_Case, Config) ->
    ?line Dog=test_server:timetrap(?default_timeout),
    [{watchdog, Dog}|Config].


fin_per_testcase(_Case, Config) ->
    Dog = ?config(watchdog, Config),
    test_server:timetrap_cancel(Dog),
    ok.

%%-----------------------------------------------------------------
%% Test Case: short integer test
%% Description: 
%%-----------------------------------------------------------------
short(doc) -> ["Description", "more description"];
short(suite) -> [];
short(_) ->
    short_big_loop([-32768, -4040, -1, 0, 4040, 32767]),
    short_little_loop([-32768, -4040, -1, 0, 4040, 32767]),
    bad_short().

short_big_loop([]) ->
    ok;
short_big_loop([X |List]) ->
    ?line [CodedType] = cdrlib:enc_short(X, []),
    ?line {X, <<>>} = cdrlib:dec_short(big, CodedType),
    short_big_loop(List),
    ok.

short_little_loop([]) ->
    ok;
short_little_loop([X |List]) ->
    ?line CodedType = enc_short_little(X, []),
    ?line {X, <<>>} = cdrlib:dec_short(little, CodedType),
    short_little_loop(List),
    ok.

enc_short_little(X, Message) -> 
    list_to_binary([(X) band 16#ff, ((X) bsr 8) band 16#ff | Message]).

bad_short() ->
    ?line {'EXCEPTION', _} = (catch cdrlib:enc_short('atom', [])),
    ?line [CodedType] = cdrlib:enc_char($a, []),
    ?line {'EXIT', _} = (catch cdrlib:dec_short(big, CodedType)),
    ok.
%%-----------------------------------------------------------------
%% Test Case: unsigned short integer test
%% Description: 
%%-----------------------------------------------------------------
ushort(doc) -> ["Description", "more description"];
ushort(suite) -> [];
ushort(_) ->
    ushort_big_loop([0, 4040, 65535]),
    ushort_little_loop([0, 4040, 65535]),
    bad_ushort().

ushort_big_loop([]) ->
    ok;
ushort_big_loop([X |List]) ->
    ?line [CodedType] = cdrlib:enc_unsigned_short(X, []),
    ?line {X, <<>>} = cdrlib:dec_unsigned_short(big, CodedType),
    ushort_big_loop(List),
    ok.

ushort_little_loop([]) ->
    ok;
ushort_little_loop([X |List]) ->
    ?line CodedType = enc_ushort_little(X, []),
    ?line {X, <<>>} = cdrlib:dec_unsigned_short(little, CodedType),
    ushort_little_loop(List),
    ok.

enc_ushort_little(X, Message) -> 
    list_to_binary([(X) band 16#ff, ((X) bsr 8) band 16#ff | Message]).

bad_ushort() ->
    ok.
%%-----------------------------------------------------------------
%% Test Case: long integer test
%% Description: 
%%-----------------------------------------------------------------
long(doc) -> ["Description", "more description"];
long(suite) -> [];
long(_) ->
    long_big_loop([-2147483648, -40404040, -32768, -4040, -1,
		   0, 4040, 32767, 40404040, 2147483647]),
    long_little_loop([-2147483648, -40404040, -32768, -4040, -1,
		      0, 4040, 32767, 40404040, 2147483647]),
    bad_long().
    

long_big_loop([]) ->
    ok;
long_big_loop([X |List]) ->
    ?line [CodedType] = cdrlib:enc_long(X, []),
    ?line {X, <<>>} = cdrlib:dec_long(big, CodedType),
    long_big_loop(List),
    ok.

long_little_loop([]) ->
    ok;
long_little_loop([X |List]) ->
    ?line CodedType = enc_long_little(X, []),
    ?line {X, <<>>} = cdrlib:dec_long(little, CodedType),
    long_little_loop(List),
    ok.

enc_long_little(X, Message) -> 
    list_to_binary([(X) band 16#ff, ((X) bsr 8) band 16#ff, ((X) bsr 16) band 16#ff,
		    ((X) bsr 24) band 16#ff | Message]).

bad_long() ->
    ok.

%%-----------------------------------------------------------------
%% Test Case: unsigned long integer test
%% Description: 
%%-----------------------------------------------------------------
ulong(doc) -> ["Description", "more description"];
ulong(suite) -> [];
ulong(_) -> 
    ulong_big_loop([0, 4040, 65535, 40404040, 2147483647, 4294967295]),
    ulong_little_loop([0, 4040, 65535, 40404040, 2147483647, 4294967295]),
    bad_ulong().
    

ulong_big_loop([]) ->
    ok;
ulong_big_loop([X |List]) ->
    ?line [CodedType] = cdrlib:enc_unsigned_long(X, []),
    ?line {X, <<>>} = cdrlib:dec_unsigned_long(big, CodedType),
    ulong_big_loop(List),
    ok.

ulong_little_loop([]) ->
    ok;
ulong_little_loop([X |List]) ->
    ?line CodedType = enc_ulong_little(X, []),
    ?line {X, <<>>} = cdrlib:dec_unsigned_long(little, CodedType),
    ulong_little_loop(List),
    ok.

enc_ulong_little(X, Message) -> 
    list_to_binary([(X) band 16#ff, ((X) bsr 8) band 16#ff, ((X) bsr 16) band 16#ff,
		    ((X) bsr 24) band 16#ff | Message]).


bad_ulong() ->
    ok.

%%-----------------------------------------------------------------
%% Test Case: long integer test
%% Description: 
%%-----------------------------------------------------------------
longlong(doc) -> ["Description", "more description"];
longlong(suite) -> [];
longlong(_) ->
    longlong_big_loop([-2147483648, -40404040, -32768, -4040, -1,
		   0, 4040, 32767, 40404040, 2147483647]),
    longlong_little_loop([-2147483648, -40404040, -32768, -4040, -1,
		      0, 4040, 32767, 40404040, 2147483647]),
    bad_longlong().
    

longlong_big_loop([]) ->
    ok;
longlong_big_loop([X |List]) ->
    ?line [CodedType] = cdrlib:enc_longlong(X, []),
    ?line {X, <<>>} = cdrlib:dec_longlong(big, CodedType),
    longlong_big_loop(List),
    ok.

longlong_little_loop([]) ->
    ok;
longlong_little_loop([X |List]) ->
    ?line CodedType = enc_longlong_little(X, []),
    ?line {X, <<>>} = cdrlib:dec_longlong(little, CodedType),
    longlong_little_loop(List),
    ok.

enc_longlong_little(X, Message) -> 
    list_to_binary([(X) band 16#ff, ((X) bsr 8) band 16#ff, ((X) bsr 16) band 16#ff,
		    ((X) bsr 24) band 16#ff, ((X) bsr 32) band 16#ff, ((X) bsr 40) band 16#ff,
		    ((X) bsr 48) band 16#ff, ((X) bsr 56) band 16#ff | Message]).

bad_longlong() ->
    ok.

%%-----------------------------------------------------------------
%% Test Case: unsigned long integer test
%% Description: 
%%-----------------------------------------------------------------
ulonglong(doc) -> ["Description", "more description"];
ulonglong(suite) -> [];
ulonglong(_) -> 
    ulonglong_big_loop([0, 4040, 65535, 40404040, 2147483647, 4294967295]),
    ulonglong_little_loop([0, 4040, 65535, 40404040, 2147483647, 4294967295]),
    bad_ulonglong().
    

ulonglong_big_loop([]) ->
    ok;
ulonglong_big_loop([X |List]) ->
    ?line [CodedType] = cdrlib:enc_unsigned_longlong(X, []),
    ?line {X, <<>>} = cdrlib:dec_unsigned_longlong(big, CodedType),
    ulonglong_big_loop(List),
    ok.

ulonglong_little_loop([]) ->
    ok;
ulonglong_little_loop([X |List]) ->
    ?line CodedType = enc_ulonglong_little(X, []),
    ?line {X, <<>>} = cdrlib:dec_unsigned_longlong(little, CodedType),
    ulonglong_little_loop(List),
    ok.

enc_ulonglong_little(X, Message) -> 
    list_to_binary([(X) band 16#ff, ((X) bsr 8) band 16#ff, ((X) bsr 16) band 16#ff,
		    ((X) bsr 24) band 16#ff, ((X) bsr 32) band 16#ff, ((X) bsr 40) band 16#ff,
		    ((X) bsr 48) band 16#ff, ((X) bsr 56) band 16#ff | Message]).

bad_ulonglong() ->
    ok.



%%-----------------------------------------------------------------
%% Test Case: boolean test
%% Description: 
%%-----------------------------------------------------------------
boolean(doc) -> ["Description", "more description"];
boolean(suite) -> [];
boolean(_) ->
    ?line [CodedTrue] = cdrlib:enc_bool('true', []),
    ?line {'true', <<>>} = cdrlib:dec_bool(CodedTrue),
    ?line [CodedFalse] = cdrlib:enc_bool('false', []),
    ?line {'false', <<>>} = cdrlib:dec_bool(CodedFalse),
    ok.

%%-----------------------------------------------------------------
%% Test Case: character test
%% Description: 
%%-----------------------------------------------------------------
character(doc) -> ["Description", "more description"];
character(suite) -> [];
character(_) ->
    ?line [Coded_0] = cdrlib:enc_char($0, []),
    ?line {$0, <<>>} = cdrlib:dec_char(Coded_0),
    ?line [Coded_a] = cdrlib:enc_char($a, []),
    ?line {$a, <<>>} = cdrlib:dec_char(Coded_a),
    ?line [Coded_Z] = cdrlib:enc_char($Z, []),
    ?line {$Z, <<>>} = cdrlib:dec_char(Coded_Z),
    ?line [Coded_dollar] = cdrlib:enc_char($$, []),
    ?line {$$, <<>>} = cdrlib:dec_char(Coded_dollar),
    ok.

%%-----------------------------------------------------------------
%% Test Case: octet test
%% Description: 
%%-----------------------------------------------------------------
octet(doc) -> ["Description", "more description"];
octet(suite) -> [];
octet(_) ->
    ?line [Coded_ff] = cdrlib:enc_octet(16#ff, []),
    ?line {16#ff, <<>>} = cdrlib:dec_octet(Coded_ff),
    ?line [Coded_00] = cdrlib:enc_octet(16#00, []),
    ?line {16#00, <<>>} = cdrlib:dec_octet(Coded_00),
    ?line [Coded_5a] = cdrlib:enc_octet(16#5a, []),
    ?line {16#5a, <<>>} = cdrlib:dec_octet(Coded_5a),
    ?line [Coded_48] = cdrlib:enc_octet(16#48, []),
    ?line {16#48, <<>>} = cdrlib:dec_octet(Coded_48),
    ok.



%%-----------------------------------------------------------------
%% Test Case: float test
%% Description: 
%%-----------------------------------------------------------------
float(doc) -> ["Description", "more description"];
float(suite) -> [];
float(_) ->
    G = 16#7fffff / 16#800000 + 1.0,
    H1 = math:pow(2, 127),
    H2 = math:pow(2, -126),
    float_big_loop([-H1 * G, -H1 * 1.0, -H2 * G, -H2 * 1.0,
		    -4040.313131, -3.141592, 0.0, 3.141592, 4040.313131,
		    H1 * G, H1 * 1.0, H2 * G, H2 * 1.0]),
    float_little_loop([-H1 * G, -H1 * 1.0, -H2 * G, -H2 * 1.0,
		       -4040.313131, -3.141592, 0.0, 3.141592, 4040.313131,
		       H1 * G, H1 * 1.0, H2 * G, H2 * 1.0]),
    ok.

float_big_loop([]) ->
    ok;
float_big_loop([X |List]) ->
    ?line [CodedType] = cdrlib:enc_float(X, []),
    ?line {Y, <<>>} = cdrlib:dec_float(big, CodedType),
    ?line float_comp(X,Y),
    float_big_loop(List),
    ok.

float_little_loop([]) ->
    ok;
float_little_loop([X |List]) ->
    ?line [CodedType] = enc_float_little(X, []),
    ?line {Y, <<>>} = cdrlib:dec_float(little, CodedType),
    ?line float_comp(X,Y),
    float_little_loop(List),
    ok.

float_comp(X,Y) when X == 0.0, Y == 0.0 ->
    ok;
float_comp(X,Y) ->
    Div = abs(Y) / abs(X),
    %% io:format("~p~n", [float_to_list(Div)]),
    ?line true = (Div < 1.0000001),
    ?line true = (Div > 0.9999999),
    ok.

enc_float_little(X, Message) -> 
    [ <<X:32/little-float>> | Message].

%%-----------------------------------------------------------------
%% Test Case: double test
%% Description: 
%%-----------------------------------------------------------------
double(doc) -> ["Description", "more description"];
double(suite) -> [];
double(_) ->
    F = 16#0fffffffffffff / 16#10000000000000 + 1.0,
    E1 = math:pow(2, 1023),
    E2 = math:pow(2, -1022),
    G = 16#7fffff / 16#800000 + 1.0,
    H1 = math:pow(2, 128),
    H2 = math:pow(2, -127),
    double_big_loop([-E1 * F, -E1 * 1.0, -E2 * F, -E2 * 1.0,
		     -H1 * G, -H1 * 1.0, -H2 * G, -H2 * 1.0,
		     -4040.313131, -3.141592, 0.0, 3.141592, 4040.313131,
		     H1 * G, H1 * 1.0, H2 * G, H2 * 1.0,
		     E1 * F, E1 * 1.0, E2 * F, E2 * 1.0]),
    double_little_loop([-E1 * F, -E1 * 1.0, -E2 * F, -E2 * 1.0,
			-H1 * G, -H1 * 1.0, -H2 * G, -H2 * 1.0,
			-4040.313131, -3.141592, 0.0, 3.141592, 4040.313131,
			H1 * G, H1 * 1.0, H2 * G, H2 * 1.0,
			E1 * F, E1 * 1.0, E2 * F, E2 * 1.0]),
    ok.

double_big_loop([]) ->
    ok;
double_big_loop([X |List]) ->
    ?line [CodedType] = cdrlib:enc_double(X, []),
    ?line {Y, <<>>} = cdrlib:dec_double(big, CodedType),
    ?line double_comp(X,Y), 
    double_big_loop(List),
    ok.

double_little_loop([]) ->
    ok;
double_little_loop([X |List]) ->
    ?line [CodedType] = enc_double_little(X, []),
    ?line {Y, <<>>} = cdrlib:dec_double(little, CodedType),
    ?line double_comp(X,Y), 
    double_little_loop(List),
    ok.

enc_double_little(X, Message) -> 
    [ <<X:64/little-float>> | Message].

double_comp(X,Y) when X == 0.0, Y == 0.0 ->
    ok;
double_comp(X,Y) ->
    Div = abs(Y) / abs(X),
    %% io:format("~p~n", [float_to_list(Div)]),
    ?line true = (Div < 1.00000000000001),
    ?line true = (Div > 0.99999999999999),
    ok.

double_should_be_ok(doc) -> ["Description", "more description"];
double_should_be_ok(suite) -> [];
double_should_be_ok(_) ->
    F = 16#0fffffffffffff / 16#10000000000000 + 1.0,
    E1 = math:pow(2, 1024), % erlang can't handle this.
    E2 = math:pow(2, -1023),
    double_big_loop([-E1 * F, -E1 * 1.0, -E2 * F, -E2 * 1.0,
		     E1 * F, E1 * 1.0, E2 * F, E2 * 1.0]),
    double_little_loop([-E1 * F, -E1 * 1.0, -E2 * F, -E2 * 1.0,
			E1 * F, E1 * 1.0, E2 * F, E2 * 1.0]),
    ok.

%%-----------------------------------------------------------------
%% Test Case: enum test
%% Description: 
%%-----------------------------------------------------------------
enum(doc) -> ["Description", "more description"];
enum(suite) -> [];
enum(_) ->
    enum_big(),
    enum_little(),
    ok.

enum_big() ->
    ?line [Coded_a] = cdrlib:enc_enum(a,[a,b,c],[]),
    ?line {a, <<>>} = cdrlib:dec_enum(big, ["a","b","c"], Coded_a),
    ?line [Coded_b] = cdrlib:enc_enum(b,[a,b,c],[]),
    ?line {b, <<>>} = cdrlib:dec_enum(big, ["a","b","c"], Coded_b),
    ?line [Coded_c] = cdrlib:enc_enum(c,[a,b,c],[]),
    ?line {c, <<>>} = cdrlib:dec_enum(big, ["a","b","c"], Coded_c),
    ok.

enum_little() ->
    ?line Coded_a = enc_r_enum(a,[a,b,c],[]),
    ?line {a, <<>>} = cdrlib:dec_enum(little, ["a","b","c"], Coded_a),
    ?line Coded_b = enc_r_enum(b,[a,b,c],[]),
    ?line {b, <<>>} = cdrlib:dec_enum(little, ["a","b","c"], Coded_b),
    ?line Coded_c = enc_r_enum(c,[a,b,c],[]),
    ?line {c, <<>>} = cdrlib:dec_enum(little, ["a","b","c"], Coded_c),
    ok.

enc_r_enum(Enum, ElemList, Message) ->
    Val = getEnumValue(ElemList,Enum, 0),
    enc_r_unsigned_long(Val, Message).

getEnumValue([Enum |_List], Enum, N) ->
    N;
getEnumValue([_ |List], Enum, N) ->
    getEnumValue(List, Enum, N + 1).

enc_r_unsigned_long(X, Message) -> 
    list_to_binary([(X) band 16#ff, ((X) bsr 8) band 16#ff,
		    ((X) bsr 16) band 16#ff, ((X) bsr 24) band 16#ff | Message]).