/******************************************************************************
 * Copyright (c) 2000-2021 Ericsson Telecom AB
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html
 *
 * Contributors:
 *   Balasko, Jeno
 *   Baranyi, Botond
 *
 ******************************************************************************/

// Tests for encoding and decoding integers as the ETSI Common Library type "IntX"
// (defined in ETSI TS 103 097)
module IntX {

/************* Type definitions *************/
type integer IntX_unsigned with { variant "IntX, COMP(nosign)"; }

type integer IntX_signed with { variant "IntX, COMP(signbit)"; }

type integer IntX_compl with { variant "IntX, COMP(2scompl)"; }

type record of IntX_signed IntXList with { variant ""; }

type record RecIntX {
  integer i,
  integer ix,
  bitstring bs
}
with {
  variant "FIELDORDER(msb)";
  variant(i) "FIELDLENGTH(16), COMP(2scompl), BITORDER(msb)";
  variant(i) "BYTEORDER(first), BITORDERINFIELD(msb)";
  variant(ix) "IntX, COMP(2scompl)";
  variant(bs) "FIELDLENGTH(8)";
}

type record RecIntXPartial {
  integer i,
  integer ix,
  bitstring bs
}
with {
  variant "FIELDORDER(msb)";
  variant(i) "FIELDLENGTH(12), BITORDER(msb)";
  variant(i) "BYTEORDER(first), BITORDERINFIELD(msb)";
  variant(ix) "IntX";
  variant(bs) "FIELDLENGTH(8)";
}

type record RecIntXPadded {
  integer i,
  integer ix,
  bitstring bs,
  integer ix2
}
with {
  variant "FIELDORDER(msb), PADDING(6), PADDALL";
  variant(i) "FIELDLENGTH(16), PADDING(5), BITORDER(msb)";
  variant(i) "BYTEORDER(first), BITORDERINFIELD(msb)";
  variant(ix) "IntX, PREPADDING(6)";
  variant(bs) "FIELDLENGTH(8)";
  variant(ix2) "IntX";
}

type component CT {}

/************* Coding functions *************/
external function f_enc_intx_u(in IntX_unsigned x) return octetstring
  with { extension "prototype(convert) encode(RAW)" }
  
external function f_dec_intx_u(in octetstring x) return IntX_unsigned
  with { extension "prototype(convert) decode(RAW)" }
  
external function f_enc_intx_s(in IntX_signed x) return octetstring
  with { extension "prototype(convert) encode(RAW)" }
  
external function f_dec_intx_s(in octetstring x) return IntX_signed
  with { extension "prototype(convert) decode(RAW)" }
  
external function f_enc_intx_c(in IntX_compl x) return octetstring
  with { extension "prototype(convert) encode(RAW)" }
  
external function f_dec_intx_c(in octetstring x) return IntX_compl
  with { extension "prototype(convert) decode(RAW)" }
  
external function f_enc_intx_list(in IntXList x) return octetstring
  with { extension "prototype(convert) encode(RAW)" }
  
external function f_dec_intx_list(in octetstring x) return IntXList
  with { extension "prototype(convert) decode(RAW)" }
  
external function f_enc_intx_rec(in RecIntX x) return octetstring
  with { extension "prototype(convert) encode(RAW)" }
  
external function f_dec_intx_rec(in octetstring x) return RecIntX
  with { extension "prototype(convert) decode(RAW)" }
  
external function f_enc_intx_rec_part(in RecIntXPartial x) return octetstring
  with { extension "prototype(convert) encode(RAW)" }
  
external function f_dec_intx_rec_part(in octetstring x) return RecIntXPartial
  with { extension "prototype(convert) decode(RAW)" }
  
external function f_enc_intx_rec_padd(in RecIntXPadded x) return octetstring
  with { extension "prototype(convert) encode(RAW)" }
  
external function f_dec_intx_rec_padd(in octetstring x) return RecIntXPadded
  with { extension "prototype(convert) decode(RAW)" }

/************* Test cases *************/
testcase tc_intx_unsigned() runs on CT
{
  // one octet
  var IntX_unsigned val := 10;
  var octetstring enc_val_exp := '0A'O;
  var octetstring enc_val := f_enc_intx_u(val);
  if (enc_val != enc_val_exp) {
    setverdict(fail, "Expected: ", enc_val_exp, ", got: ", enc_val);
  }
  var IntX_unsigned dec_val := f_dec_intx_u(enc_val_exp);
  if (dec_val != val) {
    setverdict(fail, "Expected: ", val, ", got: ", dec_val);
  }
  
  // two octets
  val := 2184;
  enc_val_exp := '8888'O;
  enc_val := f_enc_intx_u(val);
  if (enc_val != enc_val_exp) {
    setverdict(fail, "Expected: ", enc_val_exp, ", got: ", enc_val);
  }
  dec_val := f_dec_intx_u(enc_val_exp);
  if (dec_val != val) {
    setverdict(fail, "Expected: ", val, ", got: ", dec_val);
  }
  
  // ten octets
  val := 2554246324530855315;
  enc_val_exp := 'FF002372817FCA823D93'O;
  enc_val := f_enc_intx_u(val);
  if (enc_val != enc_val_exp) {
    setverdict(fail, "Expected: ", enc_val_exp, ", got: ", enc_val);
  }
  dec_val := f_dec_intx_u(enc_val_exp);
  if (dec_val != val) {
    setverdict(fail, "Expected: ", val, ", got: ", dec_val);
  }
  
  // twelve octets
  val := 7165932342958243219318534;
  enc_val_exp := 'FFC5ED71F8802FC9D0112706'O;
  enc_val := f_enc_intx_u(val);
  if (enc_val != enc_val_exp) {
    setverdict(fail, "Expected: ", enc_val_exp, ", got: ", enc_val);
  }
  dec_val := f_dec_intx_u(enc_val_exp);
  if (dec_val != val) {
    setverdict(fail, "Expected: ", val, ", got: ", dec_val);
  }
  
  setverdict(pass);
}

testcase tc_intx_signed() runs on CT
{
  // one octet
  var IntX_signed val := -10;
  var octetstring enc_val_exp := '4A'O;
  var octetstring enc_val := f_enc_intx_s(val);
  if (enc_val != enc_val_exp) {
    setverdict(fail, "Expected: ", enc_val_exp, ", got: ", enc_val);
  }
  var IntX_signed dec_val := f_dec_intx_s(enc_val_exp);
  if (dec_val != val) {
    setverdict(fail, "Expected: ", val, ", got: ", dec_val);
  }
  
  // two octets
  val := -2184;
  enc_val_exp := 'A888'O;
  enc_val := f_enc_intx_s(val);
  if (enc_val != enc_val_exp) {
    setverdict(fail, "Expected: ", enc_val_exp, ", got: ", enc_val);
  }
  dec_val := f_dec_intx_s(enc_val_exp);
  if (dec_val != val) {
    setverdict(fail, "Expected: ", val, ", got: ", dec_val);
  }
  
  // ten octets
  val := -2554246324530855315;
  enc_val_exp := 'FF402372817FCA823D93'O;
  enc_val := f_enc_intx_s(val);
  if (enc_val != enc_val_exp) {
    setverdict(fail, "Expected: ", enc_val_exp, ", got: ", enc_val);
  }
  dec_val := f_dec_intx_s(enc_val_exp);
  if (dec_val != val) {
    setverdict(fail, "Expected: ", val, ", got: ", dec_val);
  }
  
  // twelve octets
  val := -7165932342958243219318534;
  enc_val_exp := 'FFD5ED71F8802FC9D0112706'O;
  enc_val := f_enc_intx_s(val);
  if (enc_val != enc_val_exp) {
    setverdict(fail, "Expected: ", enc_val_exp, ", got: ", enc_val);
  }
  dec_val := f_dec_intx_s(enc_val_exp);
  if (dec_val != val) {
    setverdict(fail, "Expected: ", val, ", got: ", dec_val);
  }
  
  setverdict(pass);
}

testcase tc_intx_2scompl() runs on CT
{
  // one octet
  var IntX_compl val := -10;
  var octetstring enc_val_exp := '76'O;
  var octetstring enc_val := f_enc_intx_c(val);
  if (enc_val != enc_val_exp) {
    setverdict(fail, "Expected: ", enc_val_exp, ", got: ", enc_val);
  }
  var IntX_compl dec_val := f_dec_intx_c(enc_val_exp);
  if (dec_val != val) {
    setverdict(fail, "Expected: ", val, ", got: ", dec_val);
  }
  
  // two octets
  val := -2184;
  enc_val_exp := 'B778'O;
  enc_val := f_enc_intx_c(val);
  if (enc_val != enc_val_exp) {
    setverdict(fail, "Expected: ", enc_val_exp, ", got: ", enc_val);
  }
  dec_val := f_dec_intx_c(enc_val_exp);
  if (dec_val != val) {
    setverdict(fail, "Expected: ", val, ", got: ", dec_val);
  }
  
  // ten octets
  val := -2554246324530855315;
  enc_val_exp := 'FF7FDC8D7E80357DC26D'O;
  enc_val := f_enc_intx_c(val);
  if (enc_val != enc_val_exp) {
    setverdict(fail, "Expected: ", enc_val_exp, ", got: ", enc_val);
  }
  dec_val := f_dec_intx_c(enc_val_exp);
  if (dec_val != val) {
    setverdict(fail, "Expected: ", val, ", got: ", dec_val);
  }
  
  // twelve octets
  val := -7165932342958243219318534;
  enc_val_exp := 'FFDA128E077FD0362FEED8FA'O;
  enc_val := f_enc_intx_c(val);
  if (enc_val != enc_val_exp) {
    setverdict(fail, "Expected: ", enc_val_exp, ", got: ", enc_val);
  }
  dec_val := f_dec_intx_c(enc_val_exp);
  if (dec_val != val) {
    setverdict(fail, "Expected: ", val, ", got: ", dec_val);
  }
  
  setverdict(pass);
}

testcase tc_intx_record_of() runs on CT
{
  // record of IntX
  var IntXList val := { 10, -2184, -2554246324530855315, 7165932342958243219318534 };
  var octetstring enc_val_exp := '0AA888FF402372817FCA823D93FFC5ED71F8802FC9D0112706'O;
  var octetstring enc_val := f_enc_intx_list(val);
  if (enc_val != enc_val_exp) {
    setverdict(fail, "Expected: ", enc_val_exp, ", got: ", enc_val);
  }
  var IntXList dec_val := f_dec_intx_list(enc_val_exp);
  if (dec_val != val) {
    setverdict(fail, "Expected: ", val, ", got: ", dec_val);
  }
  setverdict(pass);
}

testcase tc_intx_record() runs on CT
{
  // record with IntX starting at an octet boundary
  var RecIntX val1 := { -10376, -10376, '11001100'B };
  var octetstring enc_val_exp := 'D778DFD778CC'O;
  var octetstring enc_val := f_enc_intx_rec(val1);
  if (enc_val != enc_val_exp) {
    setverdict(fail, "Expected: ", enc_val_exp, ", got: ", enc_val);
  }
  var RecIntX dec_val1 := f_dec_intx_rec(enc_val_exp);
  if (dec_val1 != val1) {
    setverdict(fail, "Expected: ", val1, ", got: ", dec_val1);
  }
  
  // record with IntX starting in a partial octet
  // (the previous field is encoded in 12 bits)
  var RecIntXPartial val2 := { 716, 716, '10101010'B };
  enc_val_exp := '2CC82CCAA0'O;
  enc_val := f_enc_intx_rec_part(val2);
  if (enc_val != enc_val_exp) {
    setverdict(fail, "Expected: ", enc_val_exp, ", got: ", enc_val);
  }
  var RecIntXPartial dec_val2 := f_dec_intx_rec_part(enc_val_exp);
  if (dec_val2 != val2) {
    setverdict(fail, "Expected: ", val2, ", got: ", dec_val2);
  }
  
  // record with two IntX fields and padding
  // (the first IntX starts after 24 bits, the second one after 54 bits)
  var RecIntXPadded val3 := { 716, 716, '10101010'B, 716 };
  enc_val_exp := '02CC0082CC2A820B30'O;
  enc_val := f_enc_intx_rec_padd(val3);
  if (enc_val != enc_val_exp) {
    setverdict(fail, "Expected: ", enc_val_exp, ", got: ", enc_val);
  }
  var RecIntXPadded dec_val3 := f_dec_intx_rec_padd(enc_val_exp);
  if (dec_val3 != val3) {
    setverdict(fail, "Expected: ", val3, ", got: ", dec_val3);
  }
  
  setverdict(pass);
}

testcase tc_intx_decode_error() runs on CT
{
  // negative tests for decoding
  // test 1: the last 2 octets from one of the signed IntX tests was removed
  var octetstring bad_enc_val := 'FF402372817FCA82'O;
  var template charstring msg_template := pattern "*While RAW-decoding type *: There are not enough bits in the buffer to decode the value of IntX type * \(needed: 64, found: 48\).";
  @try {
    var IntX_signed ret_val := f_dec_intx_s(bad_enc_val);
    setverdict(fail, "Expected dynamic test case error when decoding ", bad_enc_val,
      ", got decoded value: ", ret_val);
  }
  @catch (msg) {
    if (not match(msg, msg_template)) {
      setverdict(fail, "Expected error: ", msg_template, ", got: ", msg);
    }
  }
  
  // test 2: same value, but only the first octet is present (even the length data is incomplete)
  bad_enc_val := 'FF'O;
  msg_template := pattern "*While RAW-decoding type *: There are not enough bits in the buffer to decode the length of IntX type * \(needed: 16, found: 8\).";
  @try {
    var IntX_signed ret_val := f_dec_intx_s(bad_enc_val);
    setverdict(fail, "Expected dynamic test case error when decoding ", bad_enc_val,
      ", got decoded value: ", ret_val);
  }
  @catch (msg) {
    if (not match(msg, msg_template)) {
      setverdict(fail, "Expected error: ", msg_template, ", got: ", msg);
    }
  }
  setverdict(pass);
}

control {
  execute(tc_intx_unsigned());
  execute(tc_intx_signed());
  execute(tc_intx_2scompl());
  execute(tc_intx_record_of());
  execute(tc_intx_record());
  execute(tc_intx_decode_error());
}
 
}
with {
  encode "RAW"
}
