/******************************************************************************
 * 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:
 *   Baranyi, Botond
 *
 ******************************************************************************/

// This module contains tests for using non-deterministic external functions as
// default values for parameterized templates in Runtime 2.
// In Runtime 2 all default values are calculated when the parameterized template
// is used. Multiple parameterized template usages cause the external function's
// counter to increase multiple times. The counter is reset at the beginning of
// each test case, so tests don't depend on each other.
module TestsRT2 {

import from Resources all;

testcase tc_pard_def_int() runs on CT
{
  ef_reset();
  // the parameterized template is used 4 times here, and all 4 resulting templates are different
  var template integer temps[4] := { t_int, t_int(), t_int(-), t_int(p := -) };
  var template integer exp_temps[4] := { 1, 2, 3, 4 };
  if (valueof(temps) == valueof(exp_temps)) {
    setverdict(pass);
  }
  else {
    setverdict(fail, "Got: ", temps, ", expected: ", exp_temps);
  }
}

testcase tc_pard_def_int_expr() runs on CT
{
  ef_reset();
  var template integer temps[4] := { t_int_expr, t_int_expr(), t_int_expr(-, -), t_int_expr(p1 := -, p2 := -) };
  var template integer exp_temps[4] := { 3015, 1025, 4035, 2045 };
  if (valueof(temps) == valueof(exp_temps)) {
    setverdict(pass);
  }
  else {
    setverdict(fail, "Got: ", temps, ", expected: ", exp_temps);
  }
}

testcase tc_pard_def_str_expr() runs on CT
{
  ef_reset();
  var template charstring temps[4] := { t_str_expr, t_str_expr(), t_str_expr(-, -, -),
    t_str_expr(p1 := -, p2 := -, p3 := -) };
  var template charstring exp_temps[4] := { "A-2.3", "D-5.6", "G-8.9", "J-11.12" };
  if (valueof(temps) == valueof(exp_temps)) {
    setverdict(pass);
  }
  else {
    setverdict(fail, "Got: ", temps, ", expected: ", exp_temps);
  }
}

testcase tc_pard_def_rec_expr() runs on CT
{
  ef_reset();
  var template Rec temps[4] := { t_rec_expr, t_rec_expr(), t_rec_expr(-, -, -, -),
    t_rec_expr(pb1 := -, pb2 := -, pbs1 := -, pbs2 := -) };
  var template Rec exp_temps[4] := { { false, false, '01001100'B, '11100101'B }, { true, true, '01011100'B, '11101001'B },
    { true, false, '01101100'B, '11101101'B }, { true, false, '01111100'B, '11110001'B } };
  if (valueof(temps) == valueof(exp_temps)) {
    setverdict(pass);
  }
  else {
    setverdict(fail, "Got: ", temps, ", expected: ", exp_temps);
  }
}

testcase tc_pard_def_int_value_list() runs on CT
{
  ef_reset();
  var template integer temps[4] := { t_int_value_list, t_int_value_list(), t_int_value_list(-), t_int_value_list(p := -) };
  var template integer exp_temps[4] := { (0, 1, 25), (0, 2, 25), (0, 3, 25), (0, 4, 25) };
  if (log2str(temps) == log2str(exp_temps)) {
    setverdict(pass);
  }
  else {
    setverdict(fail, "Got: ", temps, ", expected: ", exp_temps);
  }
}

testcase tc_pard_def_int_range() runs on CT
{
  ef_reset();
  var template integer temps[4] := { t_int_range, t_int_range(), t_int_range(-), t_int_range(p := -) };
  var template integer exp_temps[4] := { (0..1), (0..2), (0..3), (0..4) };
  if (log2str(temps) == log2str(exp_temps)) {
    setverdict(pass);
  }
  else {
    setverdict(fail, "Got: ", temps, ", expected: ", exp_temps);
  }
}

testcase tc_pard_def_int_complement_list_and_range() runs on CT
{
  ef_reset();
  var template integer temps[4] := { t_int_complement_list_and_range, t_int_complement_list_and_range(),
    t_int_complement_list_and_range(-, -), t_int_complement_list_and_range(p1 := -, p2 := -) };
  var template integer exp_temps[4] := { complement (0, 1, (2..100)), complement (0, 3, (4..100)),
    complement (0, 5, (6..100)), complement (0, 7, (8..100)) };
  if (log2str(temps) == log2str(exp_temps)) {
    setverdict(pass);
  }
  else {
    setverdict(fail, "Got: ", temps, ", expected: ", exp_temps);
  }
}

testcase tc_pard_def_set() runs on CT
{
  ef_reset();
  var template Set temps[4] := { t_set, t_set(), t_set(-, -, -), t_set(p1 := -, p2 := -, p3 := -) };
  var template Set exp_temps[4] := {
    { soi1 := subset(0, 1, 2, 1), soi2 := superset(0, 2, 60), roi := { 1, permutation(0, 3, 100, 200) } },
    { soi1 := subset(0, 1, 2, 4), soi2 := superset(0, 5, 60), roi := { 1, permutation(0, 6, 100, 200) } },
    { soi1 := subset(0, 1, 2, 7), soi2 := superset(0, 8, 60), roi := { 1, permutation(0, 9, 100, 200) } },
    { soi1 := subset(0, 1, 2, 10), soi2 := superset(0, 11, 60), roi := { 1, permutation(0, 12, 100, 200) } } };
  if (log2str(temps) == log2str(exp_temps)) {
    setverdict(pass);
  }
  else {
    setverdict(fail, "Got: ", temps, ", expected: ", exp_temps);
  }
}

testcase tc_pard_def_rec_named_template_list() runs on CT
{
  ef_reset();
  var template Rec temps[4] := { t_rec_named_template_list, t_rec_named_template_list(),
    t_rec_named_template_list(-), t_rec_named_template_list(p := -) };
  var template Rec exp_temps[4] := {
    { b1 := false, b2 := ?, bs1 := '000001'B, bs2 := ? },
    { b1 := false, b2 := ?, bs1 := '000010'B, bs2 := ? },
    { b1 := false, b2 := ?, bs1 := '000011'B, bs2 := ? },
    { b1 := false, b2 := ?, bs1 := '000100'B, bs2 := ? } };
  if (log2str(temps) == log2str(exp_temps)) {
    setverdict(pass);
  }
  else {
    setverdict(fail, "Got: ", temps, ", expected: ", exp_temps);
  }
}

testcase tc_pard_def_template_lists() runs on CT
{
  ef_reset();
  var template Set temps[4] := { t_set_template_lists, t_set_template_lists(), t_set_template_lists(-, -, -),
    t_set_template_lists(p1 := -, p2 := -, p3 := -) };
  var template Set exp_temps[4] := {
    { soi1 := { 0, 1, 1 }, soi2 := { 2, 1, 300 }, roi := { [0] := 0, [3] := 3 } },
    { soi1 := { 0, 1, 4 }, soi2 := { 5, 1, 300 }, roi := { [0] := 0, [6] := 3 } },
    { soi1 := { 0, 1, 7 }, soi2 := { 8, 1, 300 }, roi := { [0] := 0, [9] := 3 } },
    { soi1 := { 0, 1, 10 }, soi2 := { 11, 1, 300 }, roi := { [0] := 0, [12] := 3 } } };
  if (log2str(temps) == log2str(exp_temps)) {
    setverdict(pass);
  }
  else {
    setverdict(fail, "Got: ", temps, ", expected: ", exp_temps);
  }
}

testcase tc_pard_def_encvalue_w_custom_encoding() runs on CT
{
  ef_reset();
  var bitstring results[4] := { encvalue(t_uni), encvalue(t_uni()), encvalue(t_uni(-)), encvalue(t_uni(p := -)) };
  var bitstring exp_results[4] := { '00000001'B, '00000010'B, '00000011'B, '00000100'B };
  if (results == exp_results) {
    setverdict(pass);
  }
  else {
    setverdict(fail, "Got: ", results, ", expected: ", exp_results);
  }
}

testcase tc_pard_def_encvalue_unichar_w_custom_encoding() runs on CT
{
  ef_reset();
  var universal charstring results[4] := { encvalue_unichar(t_uni), encvalue_unichar(t_uni()),
    encvalue_unichar(t_uni(-)), encvalue_unichar(t_uni(p := -)) };
  var universal charstring exp_results[4] := { char(0, 0, 0, 1), char(0, 0, 0, 2),
    char(0, 0, 0, 3), char(0, 0, 0, 4) };
  if (results == exp_results) {
    setverdict(pass);
  }
  else {
    setverdict(fail, "Got: ", results, ", expected: ", exp_results);
  }
}

testcase tc_pard_def_set_multi_layered() runs on CT
{
  ef_reset();
  var template Set temps[4] := { t_set_multi_layered, t_set_multi_layered(), t_set_multi_layered(-), t_set_multi_layered(p := -) };
  var template Set exp_temps[4] := {
    { soi1 := ?, soi2 := ?, roi := { 1, 1 } },
    { soi1 := ?, soi2 := ?, roi := { 1, 2 } },
    { soi1 := ?, soi2 := ?, roi := { 1, 3 } },
    { soi1 := ?, soi2 := ?, roi := { 1, 4 } } };
  if (log2str(temps) == log2str(exp_temps)) {
    setverdict(pass);
  }
  else {
    setverdict(fail, "Got: ", temps, ", expected: ", exp_temps);
  }
}

/******************************************************************/
/* Declarations and tests for bug 535729 (caused by this feature) */
/******************************************************************/

type record R1 {
  integer f1,
  boolean f2
}

type record R2 {
  integer f1,
  record of R1 f2
}

type record R3 {
  charstring f1,
  union {
    integer a1,
    R2 a2
  } f2
}

// not used during the test, but needed for code generation;
// it also has to be declared before t_pard
template R3 t_pard2(template charstring p1,
                    template integer p2) := {
  f1 := p1,
  f2 := {
    a2 := t_pard(p2)
  }
}

// has to be declared before t_default and c_default
template R2 t_pard(template integer p1,
                   template R1 p2 := t_default,
                   template R1 p3 := c_default,
                   template R1 p4 := modifies t_default := { f1 := -10 }) := {
  f1 := p1,
  f2 := { p2, p3, p4 }
};

// the init code for this template was generated into t_pard2's function
// instead of the module's init function
template R1 t_default := {
  f1 := 10,
  f2 := true
};

const R1 c_default := {
  f1 := 4,
  f2 := false
}

testcase tc_535729() runs on CT {
  var R2 x := valueof(t_pard(21)); // caused a DTE, since t_default was not initialized
  var R2 exp := {
    f1 := 21,
    f2 := {
      { f1 := 10, f2 := true },
      { f1 := 4, f2 := false },
      { f1 := -10, f2 := true }
    }
  };
  if (x == exp) {
    setverdict(pass);
  }
  else {
    setverdict(fail, x);
  }
}

control {
  execute(tc_pard_def_int());
  execute(tc_pard_def_int_expr());
  execute(tc_pard_def_str_expr());
  execute(tc_pard_def_rec_expr());
  execute(tc_pard_def_int_value_list());
  execute(tc_pard_def_int_range());
  execute(tc_pard_def_int_complement_list_and_range());
  execute(tc_pard_def_set());
  execute(tc_pard_def_rec_named_template_list());
  execute(tc_pard_def_template_lists());
  execute(tc_pard_def_encvalue_w_custom_encoding());
  execute(tc_pard_def_encvalue_unichar_w_custom_encoding());
  execute(tc_pard_def_set_multi_layered());
  execute(tc_535729());
}

}
