%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2001-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(testConstraints).

-export([int_constraints/1,refed_NNL_name/1]).


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

int_constraints(Rules) ->

    %%==========================================================
    %% SingleValue ::=  INTEGER (1) 
    %%==========================================================

    range_error(Rules, 'SingleValue', 0),
    roundtrip('SingleValue', 1),
    range_error(Rules, 'SingleValue', 2),
    range_error(Rules, 'SingleValue', 1000),

    %%==========================================================
    %% SingleValue2 ::=  INTEGER (1..20) 
    %%==========================================================

    range_error(Rules, 'SingleValue2', 0),
    roundtrip('SingleValue2', 1),
    roundtrip('SingleValue2', 20),
    range_error(Rules, 'SingleValue2', 21),
    range_error(Rules, 'SingleValue2', 1000),

    %%==========================================================
    %% SingleValue3 ::=  INTEGER (Predefined | 5 | 10)
    %% Testcase for OTP-10139. A single value subtyping of an integer type
    %% where one value is predefined.
    %%==========================================================

    roundtrip('SingleValue3', 1),
    roundtrip('SingleValue3', 5),
    roundtrip('SingleValue3', 10),

    %%==========================================================
    %% Range2to19 ::=  INTEGER (1<..<20) 
    %%==========================================================

    range_error(Rules, 'Range2to19', 1),
    roundtrip('Range2to19', 2),
    roundtrip('Range2to19', 19),
    range_error(Rules, 'Range2to19', 20),

    %%==========================================================
    %% Tests for Range above 16^4 up to maximum supported by asn1 assuming the
    %% octet length field is encoded on max 8 bits
    %%==========================================================
    LastNumWithoutLengthEncoding = 65536,
    roundtrip('Range256to65536', LastNumWithoutLengthEncoding),
    roundtrip('Range256to65536Ext', LastNumWithoutLengthEncoding),
    roundtrip('Range256to65536Ext', 42),

    FirstNumWithLengthEncoding = 65537,
    roundtrip('LargeConstraints', 'RangeMax', FirstNumWithLengthEncoding),

    FirstNumOver16_6 = 16777217,
    roundtrip('LargeConstraints', 'RangeMax', FirstNumOver16_6),
    
    FirstNumOver16_8 = 4294967297,
    roundtrip('LargeConstraints', 'RangeMax', FirstNumOver16_8),

    FirstNumOver16_10 = 1099511627776,
    roundtrip('LargeConstraints', 'RangeMax', FirstNumOver16_10),

    FirstNumOver16_10 = 1099511627776,
    roundtrip('LargeConstraints', 'RangeMax', FirstNumOver16_10),

    HalfMax = 1 bsl (128*8),
    roundtrip('LargeConstraints', 'RangeMax', HalfMax),

    Max = 1 bsl (255*8),
    roundtrip('LargeConstraints', 'RangeMax', Max),
    
    %% Random number within longlong range
    LongLong = 12672809400538808320,
    roundtrip('LongLong', LongLong),
    roundtrip('LongLongExt', LongLong),
    roundtrip('LongLongExt', -10000),

    %%==========================================================
    %%  Constraint Combinations (Duboisson p. 285)
    %%  I ::= INTEGER (0|15..269)
    %%==========================================================

    range_error(Rules, 'I', -1),
    roundtrip('I', 0),
    roundtrip('I', 15),
    roundtrip('I', 20),
    roundtrip('I', 269),
    range_error(Rules, 'I', 270),

    %%==========================================================
    %%  Constraint Combinations (Duboisson p. 285)
    %%  X1 ::= INTEGER (1..4|8|10|20)
    %%==========================================================

    range_error(Rules, 'X1', 0),
    roundtrip('X1', 1),
    roundtrip('X1', 4),
    roundtrip('X1', 8),
    roundtrip('X1', 10),
    roundtrip('X1', 20),
    range_error(Rules, 'X1', 21),

    %%==========================================================
    %%  Union of single values
    %%  Sv1 ::= INTEGER (2|3|17)
    %%  Sv2 ::= INTEGER (2|3|17, ...)
    %%  Sv3 ::= INTEGER {a(2),b(3),z(17)} (2|3|17, ...)
    %%==========================================================

    range_error(Rules, 'Sv1', 1),
    range_error(Rules, 'Sv1', 18),
    roundtrip('Sv1', 2),
    roundtrip('Sv1', 3),
    roundtrip('Sv1', 7),

    %% Encoded as root
    v_roundtrip(Rules, 'Sv2', 2),
    v_roundtrip(Rules, 'Sv2', 3),
    v_roundtrip(Rules, 'Sv2', 17),

    %% Encoded as extension
    v_roundtrip(Rules, 'Sv2', 1),
    v_roundtrip(Rules, 'Sv2', 4),
    v_roundtrip(Rules, 'Sv2', 18),

    %% Encoded as root
    v_roundtrip(Rules, 'Sv3', a),
    v_roundtrip(Rules, 'Sv3', b),
    v_roundtrip(Rules, 'Sv3', z),
    v_roundtrip(Rules, 'Sv3', 2, a),
    v_roundtrip(Rules, 'Sv3', 3, b),
    v_roundtrip(Rules, 'Sv3', 17, z),

    %% Encoded as extension
    v_roundtrip(Rules, 'Sv3', 1),
    v_roundtrip(Rules, 'Sv3', 4),
    v_roundtrip(Rules, 'Sv3', 18),

    %%==========================================================
    %%  SemiConstrained
    %%==========================================================

    roundtrip('SemiConstrained', 100),
    v_roundtrip(Rules, 'SemiConstrained', 100+128),
    roundtrip('SemiConstrained', 397249742397243),
    roundtrip('SemiConstrained', 100 + 1 bsl 128*8),
    roundtrip('SemiConstrained', 100 + 1 bsl 256*8),

    roundtrip('NegSemiConstrained', -128),
    v_roundtrip(Rules, 'NegSemiConstrained', 0),
    roundtrip('NegSemiConstrained', -1),
    roundtrip('NegSemiConstrained', 500),

    roundtrip('SemiConstrainedExt', -65536),
    roundtrip('SemiConstrainedExt', 0),
    roundtrip('SemiConstrainedExt', 42),
    v_roundtrip(Rules, 'SemiConstrainedExt', 42+128),
    roundtrip('SemiConstrainedExt', 100),
    roundtrip('SemiConstrainedExt', 47777789),
    roundtrip('SemiConstrainedExt', 42 + 1 bsl 128*8),
    roundtrip('SemiConstrainedExt', 42 + 1 bsl 256*8),

    roundtrip('NegSemiConstrainedExt', -1023),
    roundtrip('NegSemiConstrainedExt', -128),
    roundtrip('NegSemiConstrainedExt', -1),
    v_roundtrip(Rules, 'NegSemiConstrainedExt', 0),
    roundtrip('NegSemiConstrainedExt', 500),

    %%==========================================================
    %%  SIZE Constraint (Duboisson p. 268)
    %%  T ::=  IA5String (SIZE (1|2, ..., SIZE (1|2|3)))
    %%  T2 ::= IA5String (SIZE (1|2, ..., 3))
    %%==========================================================

    roundtrip('T', "IA"),
    roundtrip('T', "IAB"),
    roundtrip('T', "IABC"),
    roundtrip('T2', "IA"),
    roundtrip('T2', "IAB"),
    roundtrip('T2', "IABC"),

    %%==========================================================
    %%  More SIZE Constraints
    %%==========================================================

    roundtrip('FixedSize', <<"0123456789">>),
    roundtrip('FixedSize2', <<"0123456789">>),
    roundtrip('FixedSize2', <<"0123456789abcdefghij">>),

    range_error(Rules, 'FixedSize', "short"),
    range_error(Rules, 'FixedSize2', "short"),

    [roundtrip('VariableSize', list_to_binary(lists:seq($A, $A+L-1))) ||
	L <- lists:seq(1, 10)],

    roundtrip_enc('ShorterExt', "a", shorter_ext(Rules, "a")),
    roundtrip('ShorterExt', "abcde"),
    roundtrip('ShorterExt', "abcdef"),

    %%==========================================================
    %%  Unions of INTEGER constraints
    %%==========================================================
    seq_roundtrip(Rules, 'SeqOverlapping', 'SeqNonOverlapping', 7580),
    seq_roundtrip(Rules, 'SeqOverlapping', 'SeqNonOverlapping', 9600),
    seq_roundtrip(Rules, 'SeqOverlapping', 'SeqNonOverlapping', 18050),
    seq_roundtrip(Rules, 'SeqOverlapping', 'SeqNonOverlapping', 19000),
    seq_roundtrip(Rules, 'SeqOverlapping', 'SeqNonOverlapping', 26900),

    %%==========================================================
    %%  Constraints from object fields.
    %%==========================================================
    range_error(Rules, 'IntObjectConstr', 1),
    roundtrip('IntObjectConstr', 2),
    roundtrip('IntObjectConstr', 3),
    roundtrip('IntObjectConstr', 4),
    range_error(Rules, 'IntObjectConstr', 5),


    %%==========================================================
    %% INTEGER constraints defined using named INTEGERs.
    %%==========================================================
    42 = 'Constraints':'constrainedNamedInt-1'(),
    100 = 'Constraints':'constrainedNamedInt-2'(),
    range_error(Rules, 'ConstrainedNamedInt', 41),
    roundtrip('ConstrainedNamedInt', v1),
    range_error(Rules, 'ConstrainedNamedInt', 43),

    range_error(Rules, 'SeqWithNamedInt', {'SeqWithNamedInt',-100}),
    roundtrip('SeqWithNamedInt', {'SeqWithNamedInt',v2}),

    ok.

%% PER: Ensure that if the lower bound is Lb, Lb+16#80 is encoded
%% in two bytes as 16#0180. (Not in three bytes as 16#010080.)
v(ber, 'SemiConstrained', 100+128) -> "020200E4";
v(per, 'SemiConstrained', 100+128) -> "0180";
v(uper, 'SemiConstrained', 100+128) -> "0180";
v(ber, 'NegSemiConstrained', 0) -> "020100";
v(per, 'NegSemiConstrained', 0) -> "0180";
v(uper, 'NegSemiConstrained', 0) -> "0180";
v(ber, 'SemiConstrainedExt', 42+128) -> "020200AA";
v(per, 'SemiConstrainedExt', 42+128) -> "000180";
v(uper, 'SemiConstrainedExt', 42+128) -> "00C000";
v(ber, 'NegSemiConstrainedExt', 0) -> "020100";
v(per, 'NegSemiConstrainedExt', 0) -> "000180";
v(uper, 'NegSemiConstrainedExt', 0) -> "00C000";
v(ber, 'Sv2', 1) -> "020101";
v(per, 'Sv2', 1) -> "800101";
v(uper, 'Sv2', 1) -> "808080";
v(ber, 'Sv2', 2) -> "020102";
v(per, 'Sv2', 2) -> "00";
v(uper, 'Sv2', 2) -> "00";
v(ber, 'Sv2', 3) -> "020103";
v(per, 'Sv2', 3) -> "08";
v(uper, 'Sv2', 3) -> "08";
v(ber, 'Sv2', 4) -> "020104";
v(per, 'Sv2', 4) -> "800104";
v(uper, 'Sv2', 4) -> "808200";
v(ber, 'Sv2', 17) -> "020111";
v(per, 'Sv2', 17) -> "78";
v(uper, 'Sv2', 17) -> "78";
v(ber, 'Sv2', 18) -> "020112";
v(per, 'Sv2', 18) -> "800112";
v(uper, 'Sv2', 18) -> "808900";
v(Rule, 'Sv3', a) -> v(Rule, 'Sv2', 2);
v(Rule, 'Sv3', b) -> v(Rule, 'Sv2', 3);
v(Rule, 'Sv3', z) -> v(Rule, 'Sv2', 17);
v(Rule, 'Sv3', Val) when is_integer(Val) -> v(Rule, 'Sv2', Val).

shorter_ext(per, "a") -> <<16#80,16#01,16#61>>;
shorter_ext(uper, "a") -> <<16#80,16#E1>>;
shorter_ext(ber, _) -> none.

refed_NNL_name(_Erule) ->
    roundtrip('AnotherThing', fred),
    {error,_Reason} = 'Constraints':encode('AnotherThing', fred3).

v_roundtrip(Erule, Type, Value) ->
    Encoded = asn1_test_lib:hex_to_bin(v(Erule, Type, Value)),
    Encoded = roundtrip('Constraints', Type, Value).

v_roundtrip(Erule, Type, Value, Expected) ->
    Encoded = asn1_test_lib:hex_to_bin(v(Erule, Type, Value)),
    Encoded = asn1_test_lib:roundtrip_enc('Constraints', Type, Value, Expected).

roundtrip(Type, Value) ->
    roundtrip('Constraints', Type, Value).

roundtrip(Module, Type, Value) ->
    asn1_test_lib:roundtrip_enc(Module, Type, Value).

roundtrip_enc(Type, Value, Enc) ->
    Encoded = asn1_test_lib:roundtrip_enc('Constraints', Type, Value),
    case Enc of
	none -> ok;
	Encoded -> ok
    end.

range_error(ber, Type, Value) ->
    %% BER: Values outside the effective range should be rejected
    %% on decode.
    {ok,Encoded} = 'Constraints':encode(Type, Value),
    {error,{asn1,_}} = 'Constraints':decode(Type, Encoded),
    ok;
range_error(Per, Type, Value) when Per =:= per; Per =:= uper ->
    %% (U)PER: Values outside the effective range should be rejected
    %% on encode.
    {error,_} = 'Constraints':encode(Type, Value),
    ok.

seq_roundtrip(Rules, Seq1, Seq2, Val) ->
    Enc = roundtrip(Seq1, {Seq1,Val}),
    case Rules of
	ber ->
	    roundtrip(Seq2, {Seq2,Val});
	_ ->
	    roundtrip_enc(Seq2, {Seq2,Val}, Enc)
    end.