!
! This is the test wrapper for over the network/localhost RMIF test suite.
!
! <host>     - Host IP of SERVER. Only needs to be defined for a CLIENT that is
!              testing to a remote SERVER.
! <sPort>    - The KNOWN port of the SERVER.  CLIENT uses any available port.
! <timeout>  - The wrapper timeout period in seconds.
! <testCases>- Command line tests, supercedes test_config.tbl file (optional).
!
! /SERVER Switch determines mode of operation. Defaults to /CLIENT (local).
!
! NOTE: Requires NETTEST option tree to run complete test suite in
!       <nettest>/test/test_config.tbl, otherwise only specified test case are
!       executed or test_rmif_sample if none is specified.
!
! Usage:
! 1. First start server portion of the RMIF test suite wrapper
!    nM> %test_rmif/server sPort=9999
!
! 2. Next, run the test suite wrapper client, either on same box or another one
!    nM> %test_rmif host=hostnameOfServer sPort=9999
!
! @author  Jim Roush
!
startmacro S:host["LOCALHOST"] L:sPort[9053] D:timeout[50] S:testCases
  switch TRACE         trace        GET DEF=false    SDEF=true
  switch SERVER        mode         GET DEF="CLIENT" SDEF="SERVER"
  switch DEBUG         debug        GET DEF=false    SDEF=true
  switch DEBUGSERVER   debugs       GET DEF=^debug   SDEF=true
  switch DEBUGCLIENT   debugc       GET DEF=^debug   SDEF=true
  switch VERBOSE       verbose      GET DEF=false    SDEF=true
  switch VERBOSESERVER verboses     GET DEF=^verbose SDEF=true
  switch VERBOSECLIENT verbosec     GET DEF=^verbose SDEF=true
  switch STATEM        statemachine GET DEF=false    SDEF=true

  calc L:testPort sPort 100 +            ! Set the port for the test macro.
  set L:anyAvailablePort   -1            ! Port selection variable for the client macro.
  set S:option             "nettest"     ! Option tree where tests reside.
  set S:testConfig         "test_config" ! Name of test configuration file.
  set L:retries            10            ! This is the wrapper's RMIF retry value for lossy networks.

  set numTests {L:torun=0,L:total=0,L:run=0,L:clntfailed=0,L:srvrfailed=0,L:next=1}

  call setTestList testCases             ! Define the list of tests to run.
  call standardSetup timeout             ! Initialize client/server states and start timer.

  set ss.cntrl_server_state  ss.INIT     ! Initialize control server state variable. After standardSetup.
  set ss.cntrl_client_state  ss.INIT     ! Initialize control client state variable. After standardSetup.

  pipe on
    if mode EQS "SERVER" then
      info "^mode MODE: RMIF configured on Port=^sPort"
      rmif/id=RMIFCS/msgid=main/ppt={RETRY=^retries}/STATEM=^statemachine/VERBOSE=^verbose ^sPort
      message FUNC=send ID=MAIN NAME=CNTRL_SERVER_STARTED INFO=0 DATA={TEST=PASS}
    else
      info "^mode MODE: connecting to ^host on Port=^sPort"
      rmif/id=RMIFCC/msgid=main/ppt={RETRY=^retries}/STATEM=^statemachine/VERBOSE=^verbose anyAvailablePort
      message FUNC=sendw ID=RMIFCC NAME="OPEN" DATA={ID=CNTRLCLIENT,HP="^host:^sPort"}
      message FUNC=send ID=MAIN NAME=CNTRL_CLIENT_STARTED INFO=0 DATA={TEST=PASS}
    endif
  pipe off
  timer off
endmacro

!******************************************************************************!
procedure processMessage m:msg
  if debug then
    say "^reg.main.name: Got message name=^msg.name fid=^msg.fid info=^msg.info data=^msg.data"
  endif

!****************************** Generic Messages ********************************

  if msg.name eqs "SET" then
      if msg.data.state eqs "SERVER_STARTED" then
          message FUNC=send ID=MAIN NAME=SERVER_STARTED INFO=0 DATA={TEST=PASS}

      elseif msg.data.state eqs "CLIENT_STARTED" then
          message FUNC=send ID=MAIN NAME=CLIENT_STARTED INFO=0 DATA={TEST=PASS}

      elseif msg.data.state eqs "SERVER_DONE" then
          message FUNC=send ID=MAIN NAME=SERVER_DONE INFO=0 DATA={TEST=^msg.data.test}

      elseif msg.data.state eqs "CLIENT_DONE" then
          message FUNC=send ID=MAIN NAME=CLIENT_DONE INFO=0 DATA={TEST=^msg.data.test}

      elseif msg.data.state eqs "SERVER_TEST" then
          message FUNC=send ID=MAIN NAME=SERVER_TEST INFO=0 DATA={TEST=^msg.data.test,TEXT=^msg.data.text}

      elseif msg.data.state eqs "CLIENT_TEST" then
          message FUNC=send ID=MAIN NAME=CLIENT_TEST INFO=0 DATA={TEST=^msg.data.test,TEXT=^msg.data.text}

      elseif msg.data.state eqs "CNTRL_SERVER_STARTED" then
          message FUNC=send ID=MAIN NAME=CNTRL_SERVER_STARTED INFO=0 DATA={TEST=PASS}

      elseif msg.data.state eqs "CNTRL_CLIENT_STARTED" then
         message FUNC=send ID=MAIN NAME=CNTRL_CLIENT_STARTED INFO=0 DATA={TEST=PASS}

      elseif msg.data.state eqs "CNTRL_SERVER_TEST" then
          message FUNC=send ID=MAIN NAME=CNTRL_SERVER_TEST INFO=0 DATA={TEST=FAIL,}

      elseif msg.data.state eqs "DECREASE_MSG_COUNT" then
          message FUNC=send ID=MAIN NAME=DECREASE_MSG_COUNT INFO=0 DATA=1

      elseif msg.data.state eqs "SET_TEST_CONFIG" then
          message FUNC=send ID=MAIN NAME=SET_TEST_CONFIG INFO=0 DATA=^msg.data.config

      elseif msg.data.state eqs "TEST_LIST_READY" then
          message FUNC=send ID=MAIN NAME=CNTRL_SERVER_STARTED INFO=0 DATA={TEST=PASS}

      else
         say "^reg.main.name: unprocessed message ^msg.name from ^msg.fid"
      endif

  elseif msg.name eqs "CLOSE" then
    ! do nothing

  elseif msg.name eqs "CLOSED" then
    ! do nothing

  elseif msg.name eqs "CNTRL_SERVER_STARTED" then
      if ss.cntrl_server_state EQ ss.INIT then  ! only update status when control server is INIT state
        assert/text="^reg.main.name: Control Server started" msg.data.test isTrue
        if msg.data.test isFalse then set ss.passFail "FAIL"
        set ss.cntrl_server_state ss.STARTED
        call check_cntrl_start
      endif

  elseif msg.name eqs "CNTRL_CLIENT_STARTED" then
      if ss.cntrl_client_state EQ ss.INIT then  ! only update status when control server is INIT state
        assert/text="^reg.main.name: Control Client started" msg.data.test isTrue
        if msg.data.test isFalse then set ss.passFail "FAIL"
        set ss.cntrl_client_state ss.STARTED
        call check_cntrl_start
      endif

  elseif msg.name eqs "CNTRL_SERVER_TEST" then
    assert/text="^reg.main.name: Control Server ^msg.data.text" msg.data.test isTrue
    if msg.data.test isFalse then set ss.passFail "FAIL"

!****************************** CLIENT Messages ********************************
  elseif mode EQS "CLIENT" then
    if msg.name EQS "TIMER" then
      if ss.cntrl_client_state GE ss.STARTED
        set ss.passFail "FAIL"
        assert/text="^reg.main.name: ^cmd Error, test did not finish" FALSE !jsr
        calc L:numtests.clntfailed numtests.clntfailed+1
        if trace then
          say " "
          timer elapsed
          say "ss.passFail 1 = ^ss.passFail, numtests.run=^numtests.run &
          numtests.clntfailed=^numtests.clntfailed, numtests.srvrfailed=^numtests.srvrfailed"
          say " "
        endif

        message FUNC=sendw ID=^mode NAME=STOP_TEST INFO=0 DATA={TEST=FAIL}

        if ^numtests.total ge ^numtests.torun then
          message FUNC=send ID=MAIN NAME=STOP_^mode INFO=0 DATA={}
        else
          set ss.client_state ss.INIT          ! reset the client and server states for next test.
          set ss.server_state ss.INIT
          calc L:numTests.next numTests.next+1
          message FUNC=send ID=MAIN NAME=RUN_TEST INFO=0 DATA={COMMAND=^testvector.^{numTests.next}}
        endif
      else
        ! do nothing
      endif

    elseif msg.name eqs "OPENED" then                           ! Send list of tests to remote SERVER
      message FUNC=send ID=CNTRLCLIENT NAME=SET INFO=0 DATA={STATE=SET_TEST_CONFIG, CONFIG=^config}

    elseif msg.name eqs "SERVER_STARTED" then
      if ss.server_state EQ ss.INIT then  ! only update status when server is INIT state
        assert/text="^cmd: Server started" msg.data.test isTrue
        if msg.data.test isFalse then set ss.passFail "FAIL"
        set ss.server_state ss.STARTED
        call check_test_start
      endif

    elseif msg.name eqs "CLIENT_STARTED" then
      message FUNC=send ID=CNTRLCLIENT NAME=SET INFO=0 DATA={STATE=CLIENT_STARTED, TEST_ID=^cmd}!jsr
      if ss.client_state EQ ss.INIT then  ! only update status when client is INIT state
        assert/text="^cmd: Client started" msg.data.test isTrue
        if msg.data.test isFalse then set ss.passFail "FAIL"
        set ss.client_state ss.STARTED
        call check_test_start
      endif

    elseif msg.name eqs "RUN_TEST" then
      timer set
      set this.timer(0)     ^timeout
      set ss.passFail       "PASS"
      res cmd ^{msg.data.command}              ! set local variable to capture command name.
      info "STARTING ^{msg.data.command}"
      %TEST_^{msg.data.command}_client/id=^mode/STATEM=^statemachine/DEBUG=^debugc/VERBOSE=^verbosec/BG cntrl=this port=^testPort host=^host
      calc l:numTests.run numTests.run+1
      calc l:numTests.total numTests.total+1

    elseif msg.name eqs "SERVER_DONE" then
      if ss.server_state EQ ss.STARTED then  ! only update status when server is STARTED state
        assert/text="^cmd: Server test done" msg.data.test isTrue
        if msg.data.test isFalse then
           set ss.passFail "FAIL"
           calc L:numtests.srvrfailed numtests.srvrfailed+1
        endif
        set ss.server_state ss.DONE
        call check_test_done
      endif

    elseif msg.name eqs "CLIENT_DONE" then
      message FUNC=send ID=CNTRLCLIENT NAME=SET INFO=0 DATA={STATE=CLIENT_DONE,TEST_ID=^cmd,TEST=^msg.data.test}!jsr
      if ss.client_state EQ ss.STARTED then  ! only update status when client is STARTED state
        assert/text="^cmd: Client tests done" msg.data.test isTrue
        if msg.data.test isFalse then
           set ss.passFail "FAIL"
           calc L:numtests.clntfailed numtests.clntfailed+1
        endif
        set ss.client_state ss.DONE
        call check_test_done
      endif

    elseif msg.name eqs "STOP_CLIENT" then
      set outstr "Ran ^numtests.run macro tests, ^numtests.clntfailed tests failed."
      set outstr2 "^numtests.srvrfailed SERVER tests failed."
      if numtests.clntfailed GT 0 or numtests.srvrfailed GT 0 then
        warn "^outstr"
!        warn "^outstr2"
        warn "FAILED TESTS"
      else
        info "^outstr"
        info "ALL TESTS PASSED"
        say " "
      endif
      pipe stop

    ! test specific: rmif_remote_reuse (Client side).
    elseif msg.name eqs "CLIENT_TEST" and msg.info EQ 1 then
      ! tell RMIF server (receiver) macro to decrease it's expected packet count by 1
      message FUNC=send ID=CNTRLCLIENT NAME=SET INFO=0 DATA={STATE=DECREASE_MSG_COUNT}

    elseif msg.name eqs "SERVER_TEST" then
      if msg.data.test isFalse then set ss.passFail "FAIL"

    elseif msg.name eqs "CLIENT_TEST" then
      if msg.data.test isFalse then set ss.passFail "FAIL"

    elseif msg.name eqs "LINKERR" then
        message FUNC=send ID=MAIN NAME=CNTRL_SERVER_TEST INFO=0 DATA={TEXT="got unexpected error",TEST=FALSE}

    else
      call processStandardMessages
    endif

!****************************** SERVER Messages ********************************
  else
    if msg.name EQS "TIMER" then
      if ss.cntrl_client_state GE ss.STARTED
        set ss.passFail "FAIL"
        assert/text="^reg.main.name: ^cmd Error, test did not finish" FALSE !jsr
        calc L:numtests.srvrfailed numtests.srvrfailed+1
        if trace then
          say " "
          timer elapsed
          say "ss.passFail 2 = ^ss.passFail, numtests.run=^numtests.run &
          numtests.clntfailed=^numtests.clntfailed, numtests.srvrfailed=^numtests.srvrfailed"
          say " "
        endif

        message FUNC=sendw ID=^mode NAME=STOP_TEST INFO=0 DATA={TEST=FAIL}

        if ^numtests.total ge ^numtests.torun then
          message FUNC=send ID=MAIN NAME=STOP_^mode INFO=0 DATA={}
        else
          set ss.client_state ss.INIT          ! reset the client and server states for next test.
          set ss.server_state ss.INIT
          calc L:numTests.next numTests.next+1
          message FUNC=send ID=MAIN NAME=RUN_TEST INFO=0 DATA={COMMAND=^testvector.^{numTests.next},TEST=PASS}
        endif

      else
        !  do nothing
      endif

    elseif msg.name eqs "OPEN" then
      message FUNC=send ID=MAIN NAME=CNTRL_SERVER_STARTED INFO=0 DATA={TEST=PASS}

    elseif msg.name eqs "SET_TEST_CONFIG" then
      set numTests.torun 0
      foreach cmd inlist ^msg.data.testList
        calc l:numTests.torun numTests.torun+1
        if numTests.torun eq 1 then
          set teststring2 "^numTests.torun=^cmd"
        else
          set teststring2 "^teststring2,^numTests.torun=^cmd"
        endif
      endfor
      set t:testvector {^teststring2}
      message FUNC=send ID=REMOTE NAME=SET INFO=0 DATA={STATE=TEST_LIST_READY}
      message FUNC=send ID=MAIN NAME=CNTRL_CLIENT_STARTED INFO=0 DATA={TEST=PASS}

    elseif msg.name eqs "SERVER_STARTED" then
      message FUNC=send ID=REMOTE NAME=SET INFO=0 DATA={STATE=SERVER_STARTED, TEST_ID=^cmd}!jsr
     if ss.server_state EQ ss.INIT then  ! only update status when server is INIT state
       assert/text="^cmd: Server started" msg.data.test isTrue
       if msg.data.test isFalse then set ss.passFail "FAIL"
       set ss.server_state ss.STARTED
       call check_test_start
     endif

    elseif msg.name eqs "CLIENT_STARTED" then
      if ss.client_state EQ ss.INIT then  ! only update status when client is INIT state
        assert/text="^cmd: Client started" msg.data.test isTrue
        if msg.data.test isFalse then set ss.passFail "FAIL"
        set ss.client_state ss.STARTED
        call check_test_start
      endif

    elseif msg.name eqs "RUN_TEST" then
      timer set
      set this.timer(0)     ^timeout
      set ss.passFail       "PASS"
      res cmd ^{msg.data.command}
      info "STARTING ^{msg.data.command}"
      run "%TEST_^{msg.data.command}_server/id=^mode/STATEM=^statemachine/DEBUG=^debugs/VERBOSE=^verboses/BG cntrl=this port=^testPort"  ! start the test
      calc l:numTests.run numTests.run+1
      calc l:numTests.total numTests.total+1

    elseif msg.name eqs "SERVER_DONE" then
      message FUNC=send ID=REMOTE NAME=SET INFO=0 DATA={STATE=SERVER_DONE,TEST_ID=^cmd,TEST=^msg.data.test}
      if ss.server_state EQ ss.STARTED then  ! only update status when server is STARTED state
        assert/text="^cmd: Server test done" msg.data.test isTrue
         if msg.data.test isFalse then
           set ss.passFail "FAIL"
           calc L:numtests.srvrfailed numtests.srvrfailed+1
        endif
        set ss.server_state ss.DONE
        call check_test_done
      endif

    elseif msg.name eqs "CLIENT_DONE" then
      if ss.client_state EQ ss.STARTED then  ! only update status when client is STARTED state
        assert/text="^cmd: Client tests done" msg.data.test isTrue
        if msg.data.test isFalse then
           set ss.passFail "FAIL"
           calc L:numtests.clntfailed numtests.clntfailed+1
        endif
        set ss.client_state ss.DONE
        call check_test_done
      endif

    elseif msg.name eqs "STOP_SERVER" then
      if numtests.run GT 0 then
        set outstr "Ran ^numtests.run macro tests, ^numtests.srvrfailed tests failed."
        set outstr2 "^numtests.clntfailed CLIENT tests failed."
        if numtests.clntfailed GT 0 or numtests.srvrfailed GT 0 then
          warn "^outstr"
!          warn "^outstr2"
          warn "FAILED TESTS"
        else
          info "^outstr"
          info "ALL TESTS PASSED"
          say " "
        endif
      endif
      set ss.server_state        ss.INIT       ! Reset state variables
      set ss.client_state        ss.INIT
      set ss.cntrl_server_state  ss.INIT
      set ss.cntrl_client_state  ss.INIT
      set ss.passFail            "PASS"
      set numTests.total            0          ! Reset test counters
      set numTests.run              0
      set numTests.clntfailed       0
      set numTests.srvrfailed       0
      set numTests.next             1

      rem reg.REMOTE                           ! Remove rmif REMOTE from registry

    ! test specific: rmif_remote_reuse (Server).
    elseif msg.name eqs "DECREASE_MSG_COUNT" then
      ! supports a specific test: rmif_remote_reuse.
      message FUNC=send ID=SERVER NAME=DECREASE_MSG_COUNT INFO=0 DATA=1

    ! test specific: rmif_double_open (Server).
    elseif msg.name eqs "SERVER_TEST" and msg.info EQ 1 then
      ! tell RMIF SENDER (CLIENT) macro that we recevied a second open
      message FUNC=send ID=REMOTE NAME=SET INFO=0 &
              DATA={STATE=SERVER_TEST,TEXT=^msg.data.text,TEST=^msg.data.test}
      if msg.data.test isFalse then
        set ss.passFail "FAIL"
      endif

    elseif msg.name eqs "LINKERR" then
        ! do nothing
    else
      call processStandardMessages
    endif
  endif !*******************************************************************************
return

!--------------------------------------------------------------------------------------------------!
! Determine what tests should be performed. If command line tests are submitted then they take
! precedence over tests in the test configuration file. This method came from confidence.mm
!--------------------------------------------------------------------------------------------------!
procedure setTestList S:tests
  ! The Client should open and process the config table
  if mode EQS "CLIENT" then
    if "RMIF" SUBS ^tests then
      set t:config {TESTLIST="^tests"}
    else
      fname cfn test ^testConfig tbl ^option
      if cfn fexists
        set t:config cfn
      else
        warn "Could not find test config file: ^{cfn} - will only run 'test_rmif_sample'"
        set t:config {TESTLIST="rmif_sample"}
      endif
    endif
    foreach cmd inlist config.testList
      calc l:numTests.torun numTests.torun+1
      if numTests.torun eq 1 then
        set teststring "^numTests.torun=^cmd"
      else
        set teststring "^teststring,^numTests.torun=^cmd"
      endif
    endfor
    set t:testvector {^teststring}
  endif
return

!--------------------------------------------------------------------------------------------------!
! Check to verify that the controller macro has started. Start the first test macro if the
! controller is configured and running.
!--------------------------------------------------------------------------------------------------!
procedure check_cntrl_start
  if ss.cntrl_server_state ge ss.STARTED and ss.cntrl_client_state ge ss.STARTED then
    message FUNC=send ID=MAIN NAME=RUN_TEST INFO=0 DATA={COMMAND=^testvector.^{numTests.next}}
  endif
return

!--------------------------------------------------------------------------------------------------!
! Check to make sure that both CLIENT and SERVER parts of the test macro are in the STARTED state,
! if they are then send the start test message to the test macro.
!--------------------------------------------------------------------------------------------------!
procedure check_test_start
  if ss.server_state ge ss.STARTED and ss.client_state ge ss.STARTED then
      message FUNC=send ID=^mode NAME=START_TEST INFO=0 DATA={}
  endif
return

!--------------------------------------------------------------------------------------------------!
! Check to make sure that both CLIENT and SERVER parts of the test macro are in the DONE state.
! Send stop message to a particular macro and set test macro state back to INIT.
! Finally check to see if the controller state has been set to DONE.
!--------------------------------------------------------------------------------------------------!
procedure check_test_done
  if ss.server_state ge ss.DONE and ss.client_state ge ss.DONE then
    message FUNC=sendw ID=^mode NAME=STOP_TEST INFO=0 DATA={TEST=PASS}
    assert/text="^cmd: Client and server tests" ss.passFail isTrue
    if ss.passFail isFalse then
           calc L:numtests.clntfailed numtests.clntfailed+1
           calc L:numtests.srvrfailed numtests.srvrfailed+1
    endif
    if trace then
      say " "
      timer elapsed
      say "ss.passFail 3 = ^ss.passFail, numtests.run=^numtests.run &
      numtests.clntfailed=^numtests.clntfailed, numtests.srvrfailed=^numtests.srvrfailed"
      say " "
    endif
    if ^numtests.total ge ^numtests.torun then
      message FUNC=send ID=MAIN NAME=STOP_^mode INFO=0 DATA={TEST=PASS}
    else
      set ss.client_state ss.INIT          ! reset the client and server states for next test.
      set ss.server_state ss.INIT
      calc L:numTests.next numTests.next+1
      message FUNC=send ID=MAIN NAME=RUN_TEST INFO=0 DATA={COMMAND=^testvector.^{numTests.next}}
    endif
  endif
return

! These are a set of standard procedures
include standard_rmif_procs.mminc
