/******************************************************************************
 * Copyright (c) 2000-2025 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:
 *   Baranyi, Botond – initial implementation
 *
 ******************************************************************************/
module UnrestrictedStringTests {

import from Types all;
import from Tools all;

// unaligned
external function enc_charstr1(in CharStr1 x) return bitstring with { extension "prototype(convert) encode(PER)" }
external function enc_charstr2(in CharStr2 x) return bitstring with { extension "prototype(convert) encode(PER)" }
external function enc_charstr3(in CharStr3 x) return bitstring with { extension "prototype(convert) encode(PER)" }

external function dec_charstr1(in bitstring x) return CharStr1 with { extension "prototype(convert) decode(PER)" }
external function dec_charstr2(in bitstring x) return CharStr2 with { extension "prototype(convert) decode(PER)" }
external function dec_charstr3(in bitstring x) return CharStr3 with { extension "prototype(convert) decode(PER)" }

// aligned
external function enc_charstr1_a(in CharStr1 x) return bitstring with { extension "prototype(convert) encode(PER:ALIGNED)" }
external function enc_charstr2_a(in CharStr2 x) return bitstring with { extension "prototype(convert) encode(PER:ALIGNED)" }
external function enc_charstr3_a(in CharStr3 x) return bitstring with { extension "prototype(convert) encode(PER:ALIGNED)" }

external function dec_charstr1_a(in bitstring x) return CharStr1 with { extension "prototype(convert) decode(PER:ALIGNED)" }
external function dec_charstr2_a(in bitstring x) return CharStr2 with { extension "prototype(convert) decode(PER:ALIGNED)" }
external function dec_charstr3_a(in bitstring x) return CharStr3 with { extension "prototype(convert) decode(PER:ALIGNED)" }

testcase tc_per_unrestricted_string_encode_unaligned() runs on CT {
  // unrestricted string with 'identification' constrained to the 'fixed' alternative ('predefined' case)
  var CharStr1 x1 := { { fixed := NULL }, omit, '1234'O };
  var bitstring enc_exp := '000000100001001000110100'B;
  var bitstring enc := enc_charstr1(x1);
  if (enc != enc_exp) {
    setverdict(fail, "Encoding unrestricted string #1 failed. Got: ", enc, ", expected: ", enc_exp);
  }
  
  // unrestricted string with 'identification' constrained to the 'syntaxes' alternative ('predefined' case)
  var CharStr2 x2 := { { syntaxes := { objid_val, objid_val } }, omit, '1234'O };
  enc_exp := '000000100001001000110100'B;
  enc := enc_charstr2(x2);
  if (enc != enc_exp) {
    setverdict(fail, "Encoding unrestricted string #2 failed. Got: ", enc, ", expected: ", enc_exp);
  }
  
  // unrestricted string with no PER-visible constraints (the length restriction is ignored; 'general' case)
  var CharStr3 x3 := { { syntaxes := { objid_val, objid_val } }, omit, '1234'O };
  enc_exp := '000000000110000000000000001000000110000001100000000000000010000001100000010000100100011010000000'B;
  enc := enc_charstr3(x3);
  if (enc != enc_exp) {
    setverdict(fail, "Encoding unrestricted string #3 failed. Got: ", enc, ", expected: ", enc_exp);
  }
  var CharStr3 x4 := { { syntax := objid_val }, omit, '1234'O };
  enc_exp := '0010000001100000000000000010000001100000010000100100011010000000'B;
  enc := enc_charstr3(x4);
  if (enc != enc_exp) {
    setverdict(fail, "Encoding unrestricted string #4 failed. Got: ", enc, ", expected: ", enc_exp);
  }
  var CharStr3 x5 := { { presentation_context_id := 117 }, omit, '1234'O };
  enc_exp := '010000000010111010100000010000100100011010000000'B;
  enc := enc_charstr3(x5);
  if (enc != enc_exp) {
    setverdict(fail, "Encoding unrestricted string #5 failed. Got: ", enc, ", expected: ", enc_exp);
  }
  var CharStr3 x6 := { { context_negotiation := { 117, objid_val } }, omit, '1234'O };
  enc_exp := '01100000001011101010000001100000000000000010000001100000010000100100011010000000'B;
  enc := enc_charstr3(x6);
  if (enc != enc_exp) {
    setverdict(fail, "Encoding unrestricted string #6 failed. Got: ", enc, ", expected: ", enc_exp);
  }
  var CharStr3 x7 := { { transfer_syntax := objid_val }, omit, '1234'O };
  enc_exp := '1000000001100000000000000010000001100000010000100100011010000000'B;
  enc := enc_charstr3(x7);
  if (enc != enc_exp) {
    setverdict(fail, "Encoding unrestricted string #7 failed. Got: ", enc, ", expected: ", enc_exp);
  }
  var CharStr3 x8 := { { fixed := NULL }, omit, '1234'O };
  enc_exp := '10100000010000100100011010000000'B;
  enc := enc_charstr3(x8);
  if (enc != enc_exp) {
    setverdict(fail, "Encoding unrestricted string #8 failed. Got: ", enc, ", expected: ", enc_exp);
  }
  setverdict(pass);
}

testcase tc_per_unrestricted_string_encode_aligned() runs on CT {
  // unrestricted string with 'identification' constrained to the 'fixed' alternative ('predefined' case)
  var CharStr1 x1 := { { fixed := NULL }, omit, '1234'O };
  var bitstring enc_exp := '000000100001001000110100'B;
  var bitstring enc := enc_charstr1_a(x1);
  if (enc != enc_exp) {
    setverdict(fail, "Encoding unrestricted string #1 failed. Got: ", enc, ", expected: ", enc_exp);
  }
  
  // unrestricted string with 'identification' constrained to the 'syntaxes' alternative ('predefined' case)
  var CharStr2 x2 := { { syntaxes := { objid_val, objid_val } }, omit, '1234'O };
  enc_exp := '000000100001001000110100'B;
  enc := enc_charstr2_a(x2);
  if (enc != enc_exp) {
    setverdict(fail, "Encoding unrestricted string #2 failed. Got: ", enc, ", expected: ", enc_exp);
  }
  
  // unrestricted string with no PER-visible constraints (the length restriction is ignored; 'general' case)
  var CharStr3 x3 := { { syntaxes := { objid_val, objid_val } }, omit, '1234'O };
  enc_exp := '000000000000001100000000000000010000001100000011000000000000000100000011000000100001001000110100'B;
  enc := enc_charstr3_a(x3);
  if (enc != enc_exp) {
    setverdict(fail, "Encoding unrestricted string #3 failed. Got: ", enc, ", expected: ", enc_exp);
  }
  var CharStr3 x4 := { { syntax := objid_val }, omit, '1234'O };
  enc_exp := '0010000000000011000000000000000100000011000000100001001000110100'B;
  enc := enc_charstr3_a(x4);
  if (enc != enc_exp) {
    setverdict(fail, "Encoding unrestricted string #4 failed. Got: ", enc, ", expected: ", enc_exp);
  }
  var CharStr3 x5 := { { presentation_context_id := 117 }, omit, '1234'O };
  enc_exp := '010000000000000101110101000000100001001000110100'B;
  enc := enc_charstr3_a(x5);
  if (enc != enc_exp) {
    setverdict(fail, "Encoding unrestricted string #5 failed. Got: ", enc, ", expected: ", enc_exp);
  }
  var CharStr3 x6 := { { context_negotiation := { 117, objid_val } }, omit, '1234'O };
  enc_exp := '01100000000000010111010100000011000000000000000100000011000000100001001000110100'B;
  enc := enc_charstr3_a(x6);
  if (enc != enc_exp) {
    setverdict(fail, "Encoding unrestricted string #6 failed. Got: ", enc, ", expected: ", enc_exp);
  }
  var CharStr3 x7 := { { transfer_syntax := objid_val }, omit, '1234'O };
  enc_exp := '1000000000000011000000000000000100000011000000100001001000110100'B;
  enc := enc_charstr3_a(x7);
  if (enc != enc_exp) {
    setverdict(fail, "Encoding unrestricted string #7 failed. Got: ", enc, ", expected: ", enc_exp);
  }
  var CharStr3 x8 := { { fixed := NULL }, omit, '1234'O };
  enc_exp := '10100000000000100001001000110100'B;
  enc := enc_charstr3_a(x8);
  if (enc != enc_exp) {
    setverdict(fail, "Encoding unrestricted string #8 failed. Got: ", enc, ", expected: ", enc_exp);
  }
  setverdict(pass);
}

testcase tc_per_unrestricted_string_decode_unaligned() runs on CT {
  // unrestricted string with 'identification' constrained to the 'fixed' alternative ('predefined' case)
  var bitstring enc := '000000100001001000110100'B;
  var CharStr1 x1 := dec_charstr1(enc);
  var CharStr1 x1_exp := { { fixed := NULL }, omit, '1234'O };
  if (x1 != x1_exp) {
    setverdict(fail, "Decoding unrestricted string #1 failed. Got: ", x1, ", expected: ", x1_exp);
  }
  
  // unrestricted string with 'identification' constrained to the 'syntaxes' alternative ('predefined' case)
  var CharStr2 x2 := dec_charstr2(enc);
  var CharStr2 x2_exp := { { syntaxes := { objid_val, objid_val } }, omit, '1234'O };
  if (x2 != x2_exp) {
    setverdict(fail, "Decoding unrestricted string #2 failed. Got: ", x2, ", expected: ", x2_exp);
  }
  
  // unrestricted string with no PER-visible constraints (the length restriction is ignored; 'general' case)
  enc := '000000000110000000000000001000000110000001100000000000000010000001100000010000100100011010000000'B;
  var CharStr3 x3 := dec_charstr3(enc);
  var CharStr3 x3_exp := { { syntaxes := { objid_val, objid_val } }, omit, '1234'O };
  if (x3 != x3_exp) {
    setverdict(fail, "Decoding unrestricted string #3 failed. Got: ", x3, ", expected: ", x3_exp);
  }
  enc := '0010000001100000000000000010000001100000010000100100011010000000'B;
  var CharStr3 x4 := dec_charstr3(enc);
  var CharStr3 x4_exp := { { syntax := objid_val }, omit, '1234'O };
  if (x4 != x4_exp) {
    setverdict(fail, "Decoding unrestricted string #4 failed. Got: ", x4, ", expected: ", x4_exp);
  }
  enc := '010000000010111010100000010000100100011010000000'B;
  var CharStr3 x5 := dec_charstr3(enc);
  var CharStr3 x5_exp := { { presentation_context_id := 117 }, omit, '1234'O };
  if (x5 != x5_exp) {
    setverdict(fail, "Decoding unrestricted string #5 failed. Got: ", x5, ", expected: ", x5_exp);
  }
  enc := '01100000001011101010000001100000000000000010000001100000010000100100011010000000'B;
  var CharStr3 x6 := dec_charstr3(enc);
  var CharStr3 x6_exp := { { context_negotiation := { 117, objid_val } }, omit, '1234'O };
  if (x6 != x6_exp) {
    setverdict(fail, "Decoding unrestricted string #6 failed. Got: ", x6, ", expected: ", x6_exp);
  }
  enc := '1000000001100000000000000010000001100000010000100100011010000000'B;
  var CharStr3 x7 := dec_charstr3(enc);
  var CharStr3 x7_exp := { { transfer_syntax := objid_val }, omit, '1234'O };
  if (x7 != x7_exp) {
    setverdict(fail, "Decoding unrestricted string #7 failed. Got: ", x7, ", expected: ", x7_exp);
  }
  enc := '10100000010000100100011010000000'B;
  var CharStr3 x8 := dec_charstr3(enc);
  var CharStr3 x8_exp := { { fixed := NULL }, omit, '1234'O };
  if (x8 != x8_exp) {
    setverdict(fail, "Decoding unrestricted string #8 failed. Got: ", x8, ", expected: ", x8_exp);
  }
  setverdict(pass);
}

testcase tc_per_unrestricted_string_decode_aligned() runs on CT {
  // unrestricted string with 'identification' constrained to the 'fixed' alternative ('predefined' case)
  var bitstring enc := '000000100001001000110100'B;
  var CharStr1 x1 := dec_charstr1_a(enc);
  var CharStr1 x1_exp := { { fixed := NULL }, omit, '1234'O };
  if (x1 != x1_exp) {
    setverdict(fail, "Decoding unrestricted string #1 failed. Got: ", x1, ", expected: ", x1_exp);
  }
  
  // unrestricted string with 'identification' constrained to the 'syntaxes' alternative ('predefined' case)
  var CharStr2 x2 := dec_charstr2_a(enc);
  var CharStr2 x2_exp := { { syntaxes := { objid_val, objid_val } }, omit, '1234'O };
  if (x2 != x2_exp) {
    setverdict(fail, "Decoding unrestricted string #2 failed. Got: ", x2, ", expected: ", x2_exp);
  }
  
  // unrestricted string with no PER-visible constraints (the length restriction is ignored; 'general' case)
  enc := '000000000000001100000000000000010000001100000011000000000000000100000011000000100001001000110100'B;
  var CharStr3 x3 := dec_charstr3_a(enc);
  var CharStr3 x3_exp := { { syntaxes := { objid_val, objid_val } }, omit, '1234'O };
  if (x3 != x3_exp) {
    setverdict(fail, "Decoding unrestricted string #3 failed. Got: ", x3, ", expected: ", x3_exp);
  }
  enc := '0010000000000011000000000000000100000011000000100001001000110100'B;
  var CharStr3 x4 := dec_charstr3_a(enc);
  var CharStr3 x4_exp := { { syntax := objid_val }, omit, '1234'O };
  if (x4 != x4_exp) {
    setverdict(fail, "Decoding unrestricted string #4 failed. Got: ", x4, ", expected: ", x4_exp);
  }
  enc := '010000000000000101110101000000100001001000110100'B;
  var CharStr3 x5 := dec_charstr3_a(enc);
  var CharStr3 x5_exp := { { presentation_context_id := 117 }, omit, '1234'O };
  if (x5 != x5_exp) {
    setverdict(fail, "Decoding unrestricted string #5 failed. Got: ", x5, ", expected: ", x5_exp);
  }
  enc := '01100000000000010111010100000011000000000000000100000011000000100001001000110100'B;
  var CharStr3 x6 := dec_charstr3_a(enc);
  var CharStr3 x6_exp := { { context_negotiation := { 117, objid_val } }, omit, '1234'O };
  if (x6 != x6_exp) {
    setverdict(fail, "Decoding unrestricted string #6 failed. Got: ", x6, ", expected: ", x6_exp);
  }
  enc := '1000000000000011000000000000000100000011000000100001001000110100'B;
  var CharStr3 x7 := dec_charstr3_a(enc);
  var CharStr3 x7_exp := { { transfer_syntax := objid_val }, omit, '1234'O };
  if (x7 != x7_exp) {
    setverdict(fail, "Decoding unrestricted string #7 failed. Got: ", x7, ", expected: ", x7_exp);
  }
  enc := '10100000000000100001001000110100'B;
  var CharStr3 x8 := dec_charstr3_a(enc);
  var CharStr3 x8_exp := { { fixed := NULL }, omit, '1234'O };
  if (x8 != x8_exp) {
    setverdict(fail, "Decoding unrestricted string #8 failed. Got: ", x8, ", expected: ", x8_exp);
  }
  setverdict(pass);
}

testcase tc_per_unrestricted_string_negtest() runs on CT {
  var CharStr2 x;
  var bitstring buf;
  @try {
    buf := enc_charstr2(x);
    setverdict(fail, "CharStr #1 encoded successfully.");
  }
  @catch (msg) {
    if (not match(msg, pattern "*Encoding an unbound CHARACTER STRING value.")) {
      setverdict(fail, "Invalid error message received (#1): ", msg);
    }
  }
  buf := ''B;
  @try {
    x := dec_charstr2(buf);
    setverdict(fail, "CharStr #2 decoded successfully.");
  }
  @catch (msg) {
    if (not match(msg, pattern "*Buffer overflow while PER-decoding. Requested bits: 1, remaining bits in buffer: 0")) {
      setverdict(fail, "Invalid error message received (#2): ", msg);
    }
  }
  buf := '11100000'B;
  @try {
    x := dec_charstr3(buf);
    setverdict(fail, "CharStr #3 decoded successfully.");
  }
  @catch (msg) {
    if (not match(msg, pattern "*Decoded invalid CHOICE selection \(7\).")) {
      setverdict(fail, "Invalid error message received (#3): ", msg);
    }
  }
  setverdict(pass);
}
  
control {
  execute(tc_per_unrestricted_string_encode_unaligned());
  execute(tc_per_unrestricted_string_encode_aligned());
  execute(tc_per_unrestricted_string_decode_unaligned());
  execute(tc_per_unrestricted_string_decode_aligned());
  execute(tc_per_unrestricted_string_negtest());
}

}
