/******************************************************************************
 * 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
 *
 ******************************************************************************/
module tryCatch_Testcases {

import from tryCatch_Functions all;

external function throw(in charstring msg);

type port MyInternal_PT message { 
  inout charstring;
} with { extension "internal"}


type port PCOType message
{
  inout charstring;
}

type component MyComp {
  port MyInternal_PT PCO;
  port PCOType       ExternalPCO;
  var integer v_funcCallCounter; //for lazy
}

type record of integer RoI;

const RoI c_roi3:={1,2,3}

//Based on funtion_test :)
type union UnionType1{
  UnionType2 girl,
  integer    numero
}

type union UnionType2{
  Emma   emma,
  Eszter eszter
}

type record Emma { charstring name}
type record Eszter { charstring name}

  

//This template cannot be sent:
template charstring tr_recv := pattern "*";

//This template can be sent:
template charstring t_send := "This is the message";

//******* Types ***************
type integer MySmallInt(1..10);
//******* Constants ***********

//******* Functions ***********

function f_divByZero() {
  var integer i :=10, j:=0;
  @try {
    i:=i/j
  }
  @catch(dte_str){
    setverdict(pass);
    stop;
  }
  setverdict(fail); //never reach this line
}

function f_setverdict_str(in charstring pl_str, in template charstring pl_expected) {
  if(match(pl_str,pl_expected)) {
      setverdict(pass) 
    } else {
    setverdict(fail, match(pl_str,pl_expected));
  }
} with { extension "transparent" }


function f_tryCatch_conv() runs on MyComp {
  var charstring vl_ch1:="H" , vl_ch2:="E";
  @try{
    var hexstring vl_h:=str2hex(vl_ch1&vl_ch2);
  }
  @catch(e){
    if(match(e, pattern "*The argument of function str2hex\(\) shall contain hexadecimal digits only,*"))
    { setverdict(pass) } else {setverdict(fail)};
  }  

  //normal case:
  vl_ch1:=vl_ch2&vl_ch2
  @try{
    var hexstring vl_h:= str2hex(vl_ch1);
    setverdict(pass);
  }
  @catch(e) {
    setverdict(fail,"exception for normal value");
  }
}

function f_tryCatch_conv_imported() runs on MyComp {
  var charstring vl_ch1:="H" , vl_ch2:="E";
  template charstring tl_expected := 
    pattern "*The argument of function str2hex\(\) shall contain hexadecimal digits only,*";
  @try{
    var hexstring vl_h:=str2hex(vl_ch1&vl_ch2);
  }
  @catch(e){
    f_matchAndVerdict(e,tl_expected); //imported function
  }
}

function f_tryCatch_conv_throw() runs on MyComp {
  var charstring vl_ch1:="H" , vl_ch2:="E";
  @try{
    var hexstring vl_h:=str2hex(vl_ch1&vl_ch2);
  }
  @catch(e){
      throw(e);    
  }
}

altstep as_tryCatch_conv() runs on MyComp {
  [] PCO.receive {setverdict(pass);}
  [else] { 
      log("altstep reached the else-branch");
      var charstring vl_ch1:="H" , vl_ch2:="E";
      @try{
        var hexstring vl_h:=str2hex(vl_ch1&vl_ch2);
        setverdict(fail, "Catched DTE didn't stopped the running");
      }
      @catch(e){
        log("Catched DTE:",e);
        setverdict(pass);
        
      }
    }
}


function f_behavior_sending(in template charstring pl_msg) runs on MyComp {
  @try {
    PCO.send(pl_msg);
    if(isvalue(pl_msg) ) { 
      setverdict(pass, "no exception for sending real value template[sending template]")
    } else {
      setverdict(fail, "no exception for sending not real value template");
    }
  }
  @catch(e) {
    if( match(e, pattern "*Port PCO has neither connections nor mappings. Message cannot be sent on it.*"))
    {
      setverdict(pass);
    } else {
      log("Catched error msg:", e);
      if( isvalue(pl_msg) ) { setverdict(fail, "exception for sending real value");}
      f_setverdict_str(e, pattern "*Performing a valueof or send operation on a non-specific charstring template.");
      @try {
        PCO.send("Default msg");
      }
      @catch(f) {
        log("Catched DTE:",f); 
      }
    }
  }
  // side-effect???? :
  @try {
    var charstring vl_msg:= valueof(pl_msg);
  }
  @catch(e) {
    log("Catched DTE:",e);
    f_setverdict_str(e, pattern "*Performing a valueof or send operation on a non-specific charstring template.");

    if(isvalue(pl_msg) ) { 
      setverdict(fail, "exception for valueof(real value template)");
    } else {
      setverdict(pass, "exception for valueof( not real value template[receiving template])");
    }
  }
}

modulepar integer tsp_counterLimit := 10000
function f_behavior_sendingInfinite(in template charstring pl_msg) runs on MyComp {
  var integer vl_counter:=0;
  while(vl_counter < tsp_counterLimit) {
    @try {
      PCO.send(valueof(pl_msg)&int2str(vl_counter));
    }
    @catch(e) {
      log("Catched DTE:",e); 
      break;
    }
    vl_counter:=vl_counter+1;
     
    if ( (vl_counter mod 10000) ==0) { log(">>>Counter:",vl_counter); }
  }
  log("Bye");
}


function f_behavior_receiving(in template charstring pl_expected) runs on MyComp {
  timer T := 2.0;
  T.start;
  alt {
    [] PCO.receive { log("msg received"); setverdict(pass) };
    [] T.timeout { setverdict(none, "timeout"); }
  }
}

function f_behavior_notReceiving(in template charstring pl_expected) runs on MyComp {
  timer T := 10.0;
  T.start;
  alt {
    //[] PCO.receive { log("msg received"); setverdict(pass) };
    [] T.timeout { setverdict(none, "timeout"); }
  }
}

//sending on external port (i.e not internal port)
function f_behavior_externalSending(in template charstring pl_msg) runs on MyComp {
  @try {
    ExternalPCO.send(pl_msg);
    if(isvalue(pl_msg) ) { 
      setverdict(pass, "no exception for sending real value template[sending template]")
    } else {
      setverdict(fail, "no exception for sending not real value template");
    }
  }
  @catch(e) {
    log("Catched DTE:",e);
    if( match(e, pattern "*Port PCO has neither connections nor mappings. Message cannot be sent on it.*"))
    {
      setverdict(pass);
    } else {
      if( isvalue(pl_msg) ) { 
        setverdict(fail, "exception for sending real value");
      } else {
        setverdict(pass, "exception for sending not real value");
      }
    }
   }
}

function f_behavior_externalReceiving(in template charstring pl_expected) runs on MyComp {
  timer T := 10.0;
  T.start;
  alt {
    [] PCO.receive { log("msg received"); setverdict(pass) };
    [] T.timeout { setverdict(none, "timeout"); }
  }
}

function f_valueof(in template charstring pl_msg) runs on MyComp {
  @try {
    var charstring vl_msg:= valueof(pl_msg);
    if(isvalue(pl_msg) ) { 
      setverdict(pass, "no exception for valueof( real value template[sending template])")
    } else {
      setverdict(fail, "no exception for valueof(not real value template)");
    }    
  }
  @catch(e) {
    log("Catched DTE:", e);
    f_setverdict_str(e, pattern "*Performing a valueof or send operation on a non-specific charstring template.");

    if(isvalue(pl_msg) ) { 
      setverdict(fail, "exception for valueof of real value template");
    } else {
      setverdict(pass, "exception for valueof of not real value template[receiving template]");
    }
  }
}

function f_assignment_int(inout integer pl_left, in integer pl_right ) {
  @try {
    pl_left:=pl_right;
    if( not isbound(pl_right) ) { 
      log("right value:",pl_right);
      setverdict(fail, "no exception for Assignment of an unbound value " ) 
    } else {
      setverdict(pass, "exception for Assignment of a bound value");//this line never reached
    }
  }
  @catch(m){
    log("Catched DTE:",m);
    if( not isbound(pl_right) ) { 
      setverdict(pass, "exception for Assignment of an unbound value." ) 
    } else { 
      setverdict(fail,"no exception for Assignment of a bound value.") }
    f_setverdict_str(m, pattern "*Assignment of an unbound integer value.");
  }
}

//counter counts the number of function calls
//ther is no evaluation in try-catch and there is one evaluation if pl_evalFlag is true
function f_returnsThreeAndCounts()  runs on MyComp return integer{
  v_funcCallCounter:=v_funcCallCounter+1;
  return 1;
}

function f_lazy_withTryCatch_exprNotEval( in @lazy integer pl_i, in boolean pl_evalFlag) runs on MyComp {
  var integer vl_i:=0;  
  var RoI vl_roi1:={1,2};
  @try {
    vl_i:=vl_roi1[10]; //DTE!!!
    vl_i:= pl_i; //expression is not evaluated
    setverdict(fail);
  }
  @catch(e) {
    log("Catched DTE:",e);
    setverdict(pass);
  }
  if(pl_evalFlag) {
    vl_i:= pl_i;
  }

  log("Inside lazy:", vl_i);
}


//******* Testcases ***********

//The exception is thrown by date conversion DTE
group g_divByZero {
testcase tc_tryCatchInTc_divByZero() runs on MyComp {
  var integer i :=10, j:=0;
  @try {
    i:=i/j
  }
  @catch(dte_str){
    setverdict(pass);
    stop;
  }
  setverdict(fail); //never reach this line
}

testcase tc_tryCatchInFunc_divByZero() runs on MyComp {
  f_divByZero();
}

}//g_divByZero

group g_indexOverflow {

testcase tc_tryCatchInTc_indexOverflow1() runs on MyComp {
  var RoI vl_roi :={}
  @try{
    log(vl_roi[0]);
  }
  @catch(dte_str){
    log("Catched DTE:",dte_str);
    setverdict(pass);
    stop;
  }
  setverdict(fail);
}

testcase tc_tryCatchInTc_indexOverflow2() runs on MyComp {
  var RoI vl_roi :={1,2,3}
  @try{
    log(vl_roi[3]);
    setverdict(fail);
  }
  @catch(dte_str){
    log("Catched DTE:",dte_str);
    setverdict(pass);
    stop;
  }
  setverdict(fail);
}  

testcase tc_tryCatchInTc_indexNegative() runs on MyComp {
  var RoI vl_roi :={1,2,3}
  var integer vl_i :=-1;
  @try{
    log(vl_roi[vl_i]);
    setverdict(fail);
  }
  @catch(dte_str){
    log("Catched DTE:",dte_str);
    setverdict(pass);
    stop;
  }
  setverdict(fail);
}


}//g_indexOverflow

group g_union {

testcase tc_tryCatchInTc_unionInvalidAccess1() runs on MyComp {
  var UnionType1 vl_e := {girl := {emma := {name := "Emma"}}};
  @try {
    log(log2str("numero: ",vl_e.numero));
    setverdict(fail, "no exception at Using non-selected union field");
  }
  @catch(e) {
    log("Catched DTE:",e);
    setverdict(pass, "exception at Using non-selected union field");
  }

}

testcase tc_tryCatchInTc_unionInvalidAccess2() runs on MyComp {
  var UnionType1 vl_e := {girl := {emma := {name := "Emma"}}};
  var integer i:=1;
  @try {
    i:= vl_e.numero;
    setverdict(fail, "no exception at Using non-selected union field" );
  }
  @catch(e) {
    log("Catched DTE:",e);
    setverdict(pass, "exception at Using non-selected union field");
  }
    
}

testcase tc_tryCatchInTc_templatePatternInvalidAccess_hex() runs on MyComp {
  var template hexstring vth := 'AABB?CC'H  // It's a pattern, cannot be indexed, runtime error.
  @try {
    vth[0] := 'B'H;
    setverdict(fail);
  }
  @catch(e) {
    log("Catched DTE:",e);
    setverdict(pass, "exception at Accessing a hexstring element of a non-specific hexstring template");
  }
}

}//g_union

group g_conversion {
  
//this testcase checks if a try-catch works within a testcase
testcase tc_tryCatchInTc_conv() runs on MyComp {
  var charstring vl_ch1:= "11", vl_ch2:="a";
  @try{
   var integer vl_i:= str2int(vl_ch1&vl_ch2);
  }
  @catch(e) {
    log("Catched DTE:",e);
    setverdict(pass);
  }

  //normal case:
  @try{
    var integer vl_i:= str2int(vl_ch1);
    setverdict(pass);
  }
  @catch(e) {
    log("Catched DTE:",e);
    setverdict(fail,"exception for normal value");
  }
  
}

//this testcase checks if a try-catch works within a function
testcase tc_tryCatchInFunc_conv() runs on MyComp {
  f_tryCatch_conv();
}

//this testcase checks if a catch block can call imported functions
testcase tc_tryCatchInFunc_conv_imported() runs on MyComp {
  f_tryCatch_conv_imported();
}

//this testcase checks if an exception thrown from another function will be catched or not
testcase tc_tryCatchInFunc_conv_throw() runs on MyComp {
  @try{
    f_tryCatch_conv_throw();
  }
  @catch(f){
    if(match(f, pattern "*The argument of function str2hex\(\) shall contain hexadecimal digits only,*"))
    { 
      setverdict(pass); 
      stop;
    } else 
    {
      setverdict(fail)
    };
  }
  setverdict(fail); //this line must not reached!
}

//this testcase checks if a try-catch works within an altstep
testcase tc_tryCatchInAltstep_conv() runs on MyComp {
  as_tryCatch_conv();
}

}//g_conversion

//***** send-recv ********

//this testcase checks if a try-catch without DTE work or not for sending & receiving

group g_send_recv {
  
testcase tc_tryCatchInTc_send_positive() runs on MyComp {
  var MyComp vc_A:=MyComp.create("Sending1");
  var MyComp vc_B:=MyComp.create("Receiving1"); 
  connect(vc_A:PCO,vc_B:PCO);
  vc_B.start(f_behavior_receiving(tr_recv));
  vc_A.start(f_behavior_sending(t_send));
  all component.done;
  setverdict(pass);
  
}

testcase tc_tryCatchInTc_send_neg() runs on MyComp {
  var MyComp vc_A:=MyComp.create("Sending2");
  var MyComp vc_B:=MyComp.create("Receiving2"); 
  connect(vc_A:PCO,vc_B:PCO);
  vc_B.start(f_behavior_receiving(tr_recv));
  vc_A.start(f_behavior_sending(tr_recv));
  all component.done;
  setverdict(pass);
}

testcase tc_tryCatchInTc_send_positive_notConnected() runs on MyComp {
  var MyComp vc_A:=MyComp.create("Sending3");
  var MyComp vc_B:=MyComp.create("Receiving3"); 
  //>>>>>>>>>>>connect(vc_A:PCO,vc_B:PCO);
  vc_B.start(f_behavior_receiving(tr_recv));
  vc_A.start(f_behavior_sending(t_send));
  all component.done;
  setverdict(pass);
  
}

testcase tc_tryCatchInTc_send_positive_noReceivingSide() runs on MyComp {
  var MyComp vc_A:=MyComp.create("Sending4");
  var MyComp vc_B:=MyComp.create("Receiving4"); 
  connect(vc_A:PCO,vc_B:PCO);
  //vc_B.start(f_behavior_receiving(tr_recv));
  vc_A.start(f_behavior_sending(t_send));
  all component.done;
  disconnect(vc_A:PCO,vc_B:PCO);
  setverdict(pass);

}

testcase tc_tryCatchInTc_send_positive_noReceivingSide_InfiniteSending() runs on MyComp {
  var MyComp vc_A:=MyComp.create("Sending5");
  var MyComp vc_B:=MyComp.create("Receiving5"); 
  connect(vc_A:PCO,vc_B:PCO);
  vc_B.start(f_behavior_notReceiving(tr_recv));
  vc_A.start(f_behavior_sendingInfinite(t_send));
  all component.done;
  disconnect(vc_A:PCO,vc_B:PCO);
  setverdict(pass);

}

testcase tc_tryCatchInTc_mapping() runs on MyComp {
  var MyComp vc_A:=MyComp.create("Sending6");
  var MyComp vc_B:=MyComp.create("Receiving6"); 
  @try {
    map(vc_A:ExternalPCO,vc_B:ExternalPCO);
  }
  @catch(e){
    log("Catched DTE:",e);
    setverdict(pass);
    stop;
  }
  setverdict(fail);
}

testcase tc_tryCatchInTc_unmapping() runs on MyComp {
  var MyComp vc_A:=MyComp.create("Sending6");
  var MyComp vc_B:=MyComp.create("Receiving6"); 
  connect(vc_A:PCO,vc_B:PCO);
  map(vc_A:ExternalPCO,system:ExternalPCO);
  map(vc_B:ExternalPCO,system:ExternalPCO);
  @try {
    unmap(vc_A:ExternalPCO,vc_B:ExternalPCO);
    setverdict(fail);
  }
  @catch(e){
    log("Catched DTE:",e);
    setverdict(pass)
  }
}

testcase tc_tryCatchInTc_externalSend_positive() runs on MyComp {
  var MyComp vc_A:=MyComp.create("Sending6");
  var MyComp vc_B:=MyComp.create("Receiving6"); 
  connect(vc_A:PCO,vc_B:PCO);
  map(vc_A:ExternalPCO,system:ExternalPCO);
  map(vc_B:ExternalPCO,system:ExternalPCO);
  vc_B.start(f_behavior_externalReceiving(tr_recv));
  vc_A.start(f_behavior_externalSending(t_send));
  all component.done;
  unmap(vc_A:ExternalPCO,system:ExternalPCO);
  unmap(vc_B:ExternalPCO,system:ExternalPCO);
  disconnect(vc_A:PCO,vc_B:PCO);
  setverdict(pass);
}

//this testcase checks if exception is thrown when the sending puffer is overloaded
testcase tc_tryCatchInTc_externalSend_noReceiving_InfiniteSending() runs on MyComp {
  map(self:ExternalPCO,system:ExternalPCO);
  var integer i;
  @try {
    for(i:=0; i<1000000; i:=i+1) {
      ExternalPCO.send("Hello"&int2str(i));
    }
    setverdict(pass, "No stack overflow happened");
  }
  @catch(e){
    log("Catched DTE:",e);
    setverdict(pass)
  }
  //f_behavior_externalSending(t_send);
  unmap(self:ExternalPCO,system:ExternalPCO);
}

}//g_send_recv

group g_valueof {
  
testcase tc_tryCatchInFunc_valueof_pos() runs on MyComp {
  f_valueof(t_send);
}

testcase tc_tryCatchInFunc_valueof_neg() runs on MyComp {
  f_valueof(tr_recv);
}

}//g_valueof


group g_assignment {
  
testcase tc_tryCatchInTc_assignment_integer() runs on MyComp {
  var integer i,j;
  @try {
    j:=i; //uninitialized right value
    setverdict(fail,"no exception for Assignment of an unbound value" ); //do not executed!
  }
  @catch(m) {
    log("Catched DTE:",m);
    f_setverdict_str(m, pattern "*Assignment of an unbound integer value.");
    //setverdict(pass);
  }
  //normal case:
  i:=1;
  @try {
    j:=i;
    setverdict(pass,"no exception for Assignment of a not unbound value" );
  }
  @catch(m) {
    log("Catched DTE:",m);
    setverdict(fail, "exception for Assignment of an unbound value");
  }
}

testcase tc_tryCatchInFunc_assignment_integer() runs on MyComp {
  var integer i,j;
  f_assignment_int(i,j);//unbound assignment
  j:=1;
  f_assignment_int(i,j);//bound assignment  
}

}//g_assignment


//this testcase checks the cooperation of the @lazy and the @try-catch functionality
testcase tc_tryCatchInFunc_lazy1() runs on MyComp {
  v_funcCallCounter:=0;
  f_lazy_withTryCatch_exprNotEval(2*f_returnsThreeAndCounts(), true);
  if(v_funcCallCounter==1) {setverdict(pass)} else {setverdict(fail)};
}

testcase tc_tryCatchInFunc_lazy2() runs on MyComp {
  v_funcCallCounter:=0;
  f_lazy_withTryCatch_exprNotEval(2*f_returnsThreeAndCounts(), false);
  if(v_funcCallCounter==0) {setverdict(pass)} else {setverdict(fail)};
}


//******* Control Part ********
control {
  var integer vl_i:=3, vl_j:=6;
  var MySmallInt vl_s := vl_i*vl_j;// run-time not checked 
  var charstring vl_ch1:= "11", vl_ch2:="a";
  @try{
  vl_i:= str2int(vl_ch1&vl_ch2);
  }
  @catch(e) {
    log("Catched DTE:",e);
    //no setverdict can be applied here
  }

  execute(tc_tryCatchInTc_divByZero());
  execute(tc_tryCatchInFunc_divByZero());
  execute(tc_tryCatchInTc_indexOverflow1());
  execute(tc_tryCatchInTc_indexOverflow2());
  execute(tc_tryCatchInTc_indexNegative());
  execute(tc_tryCatchInTc_unionInvalidAccess1());
  execute(tc_tryCatchInTc_unionInvalidAccess2()); 
  execute(tc_tryCatchInTc_templatePatternInvalidAccess_hex()); 
  execute(tc_tryCatchInTc_conv());
  execute(tc_tryCatchInFunc_conv());
  execute(tc_tryCatchInFunc_conv_imported());
  execute(tc_tryCatchInFunc_conv_throw());
  execute(tc_tryCatchInAltstep_conv());
  execute(tc_tryCatchInTc_send_positive());
  execute(tc_tryCatchInTc_send_neg());
  execute(tc_tryCatchInTc_send_positive_notConnected());
  execute(tc_tryCatchInTc_send_positive_noReceivingSide());
  //execute(tc_tryCatchInTc_send_positive_noReceivingSide_InfiniteSending());
  execute(tc_tryCatchInTc_mapping());
  execute(tc_tryCatchInTc_unmapping());
  execute(tc_tryCatchInTc_externalSend_positive());  
  //execute(tc_tryCatchInTc_externalSend_noReceiving_InfiniteSending());
  execute(tc_tryCatchInFunc_valueof_pos());
  execute(tc_tryCatchInFunc_valueof_neg());
  execute(tc_tryCatchInTc_assignment_integer());
  execute(tc_tryCatchInFunc_assignment_integer());
  execute(tc_tryCatchInFunc_lazy1());
  execute(tc_tryCatchInFunc_lazy2());
}

}
