!******************************************************************************!
! Test the TIMEX command
!
! @author J. Czechowski IV / Neon Ngo
!******************************************************************************!
startmacro
  switch INTERACTIVE interAct GET DEF=TRUE  SDEF=TRUE  ! for normal interactive test(s)
  switch BASELINE    baseline GET DEF=FALSE SDEF=TRUE  ! for defects or more obscure test case(s)

  call test_SOD
  call test_time      ! <-- This used to be the entire macro.
  call test_types
  call test_formats
  call test_getTime
  call test_sp24      ! Special case 0=00:00:00 to 86400=24:00:00 (Bug 1250)
  call test_rounding  ! Bug 1252
  call test_day
  call test_leap_year ! Bug 3000

  if interAct then
    ! Interactive test for Bug 1601 that checks GHA in old files.
    res inst "Tests 1-3 should have no warnings. Test 4 should warn that the file 'appears &
              to have incorrect GHA in quadwords'."

    pipe on
      panel/msgid=MAIN/setup/controls=gc
      gcontrol label  macro   "MACRO"
      gcontrol menu main "Exit with Status = " "PASS,FAIL" /tleft
      set gc.main.itemColors {PASS=GREEN,FAIL=RED}
      gcontrol text idtext "INSTRUCTIONS" "^inst" 24 7 /title

      shellgui/id=idshellgui/attach
      say ""
      info "Verify the following: "
      say "==== TEST 1: NO OUTPUT ==="
      file open  f1 nxm.sys.test.test_timex_good_gha.prm  ! <-- checks GHA
      file close f1
      say "==== TEST 2: NO OUTPUT ==="
      file open  f2 nxm.sys.test.test_timex_ok_gha.prm  ! <-- checks GHA
      file close f2
      say "==== TEST 3: NO OUTPUT ==="
      file open  f3 nxm.sys.test.test_timex_new_gha.prm  ! <-- checks GHA
      file close f3
      say "==== TEST 4: WARNING ==="
      file open  f4 nxm.sys.test.test_timex_bad_gha.prm  ! <-- checks GHA
      file close f4
    pipe off
  endif

  %test_timex_time_relative
endmacro

!******************************************************************************!
procedure processMessage m:msg
  !say "msg.name ^msg.name, data ^msg.data"

  if msg.name eqs "MAIN" then
    assert/text="^reg.main.name: Tests for EXPLAIN" msg.data eqss "PASS"
    pipe stop
  else
    say "^reg.main.name: Unhandled message name: ^msg.name from fid: ^msg.fid data: ^msg.data"
  endif
return

!******************************************************************************!
procedure test_time
  !displays the current time at the terminal.
  TIMEX NOW S:currentTime
  assert/text="^reg.main.name: NOW - current time string length" currentTime.length EQ 24

  remove dtime
  TIMEX NOW D:dtime
  assert/text="^reg.main.name: current time as DOUBLE" dtime REXISTS
  remove dtime currentTime

  ! displays the current time rounded to the nearest second.
  TIMEX 2007:08:23:13:55:31.4 round=1 S:currentTimeR1
  TIMEX 2007:08:23:13:55:31.5 round=1 S:currentTimeR2
  assert/text="^reg.main.name: ROUNDED time string" currentTimeR1 EQ 2007:08:23:13:55:31
  assert/text="^reg.main.name: ROUNDED time string" currentTimeR2 EQ 2007:08:23:13:55:32
  remove currentTimeR1 currentTimeR2

  ! writes the current time to the epoch timecode fields and a result.
  res myfile "ram:temp_timex"
  header/create myfile
  TIMEX NOW file=myfile localtime
  remove timecodePrec
  key myfile get TC_PREC timecodePrec
  set D:dtime ^localtime
  assert/text="^reg.main.name: insert file timecode (exists)" timecodePrec REXISTS
  ! using EQT with tolerance of 1e-6 for up to microsecond precission as per BLUE file CCD
  assert/text="^reg.main.name: insert file timecode "/TOL=1e-6 FILE(myfile).timecode EQT dtime
  remove dtime localtime timecodePrec

  ! copies the epoch timecode in MYFILE to the epoch timecode in YOURFILE and
  ! the YOUROTHERFILEs acquisition fields.
  set epochTCfile "temp_timex_epochcopy"
  set acqTCfile "temp_timex_acqcopy"
  header/create epochTCfile
  header/create acqTCfile
  TIMEX infile=myfile file=epochTCfile acq=acqTCfile
  assert/text="^reg.main.name: copy file EPOCH timecode" &
       FILE(myfile).timecode EQ FILE(epochTCfile).timecode
  warning "^reg.main.name: jph --  ACQ timecode - NOT IMPLEMENTED!"
  !!!assert/text="^reg.main.name: copy file ACQ timecode - NOT IMPLEMENTED! " FALSE
  erase epochTCfile acqTCfile
  remove epochTCfile acqTCfile

  ! uses the timecode field in MYFILE as the quadword epoch time for the ECI
  ! reference frame.  This affects the epoch_year, epoch_seconds, and
  ! epoch_hour_angle quadwords of the file, quadwords(10-12).
  header/create type5000file type=5000
  TIMEX NOW file=type5000file

  ! The YEAR, SEC, and GHA values should match X-Midas. As part of Bug 1601
  ! tests for these compuations were added to the unittests for nxm.sys.lib.Time.
  TIMEX infile=type5000file quad=type5000file year=year gha=gha SoY=soy
  invoke Q_YEAR nxm.sys.lib.DataFile.Q_YEAR
  invoke Q_SEC  nxm.sys.lib.DataFile.Q_SEC
  invoke Q_GHA  nxm.sys.lib.DataFile.Q_GHA
  assert/text="^reg.main.name: QUADWORD (Q_YEAR)" year EQ FILE(type5000file).quadword(Q_YEAR)
  assert/text="^reg.main.name: QUADWORD (Q_SEC)"  soy  EQ FILE(type5000file).quadword(Q_SEC)
  assert/text="^reg.main.name: QUADWORD (Q_GHA)"  gha  EQ FILE(type5000file).quadword(Q_GHA)

  ! breaks out the acquisition date/time keywords for MYFILE into its Julian day
  ! components for system modeling functions.
  TIMEX infile=myfile &
        JD=julianDay &
        MJD=modifiedJulianday &
        MJS=modifiedJuliansecond &
        SEC=sec_modifiedJuliansecond &
        YEAR=year &
        DOY=dayofyear &
        SOY=secondsofyear &
        SOD=secondsofday &
        FOD=fractionOfDay &
        GHA=greenwichhourangle &
        WSEC=WholeSecondsSinceJ1950 &
        FSEC=FracSecsSinceJ1950

  assert/text="^reg.main.name: extract various outputs" &
     Julianday                   REXISTS AND &
     modifiedJulianday           REXISTS AND &
     modifiedJuliansecond        REXISTS AND &
     sec_modifiedJuliansecond    REXISTS AND &
     year                        REXISTS AND &
     dayofyear                   REXISTS AND &
     secondsofyear               REXISTS AND &
     secondsofday                REXISTS AND &
     fractionOfDay               REXISTS AND &
     greenwichhourangle          REXISTS AND &
     WholeSecondsSinceJ1950      REXISTS AND &
     FracSecsSinceJ1950          REXISTS AND &
     modifiedJuliansecond        EQ sec_modifiedJuliansecond

  erase myfile
  erase type5000file
return


!*******************************************************************************
! DR #509787-1 - The SOD (seconds of day) function used to use the IEEERemainder
! method in java.lang.Math.  This method may return a NEGATIVE number and thus
! did notalways give the correct SOD
procedure test_SOD
  set S:testSOD "2004:11:15::13:55:31.503"
  timex ^testsod sod=tempSOD
  assert/text="^reg.main.name: SOD function" tempSOD GT 0 AND tempSOD EQT 50131.503
return

!*******************************************************************************
! Tests type conversion
procedure test_types
  res s:myDateStr  "2004:11:15::13:55:31.503"
  res d:myDate     ^myDateStr
  res L:myDateLong ^myDateStr

  !!! TEST INPUT CONVERSION
  timex myDate wsec=temp
  assert/text="^reg.main.name: Given DOUBLE result" temp eq 1.731678931E9

  timex "^myDate" wsec=temp
  assert/text="^reg.main.name: Given DOUBLE string" temp eq 1.731678931E9

  timex ^myDate wsec=temp
  assert/text="^reg.main.name: Given DOUBLE value" temp eq 1.731678931E9


  timex myDateStr wsec=temp
  assert/text="^reg.main.name: Given STRING result" temp eq 1.731678931E9

  timex "^myDateStr" wsec=temp
  assert/text="^reg.main.name: Given STRING string" temp eq 1.731678931E9

  timex ^myDateStr wsec=temp
  assert/text="^reg.main.name: Given STRING value" temp eq 1.731678931E9

  !!! TEST OUTPUT CONVERSION
  timex ^myDateStr temp
  assert/text="^reg.main.name: Output as DOUBLE (default)" temp eq myDate

  timex ^myDateStr d:temp
  assert/text="^reg.main.name: Output as DOUBLE (specified)" temp eq myDate

  timex ^myDateStr L:temp       ! <-- Will round up
  assert/text="^reg.main.name: Output as LONG (round down)" temp eq 1731678932

  timex 1.731678931503E9 L:temp ! <-- Will round up (same as ^myDateStr)
  assert/text="^reg.main.name: Output as LONG (round down)" temp eq 1731678932

  timex 1.731678931203E9 L:temp ! <-- Will round down
  assert/text="^reg.main.name: Output as LONG (round up)" temp eq 1731678931
return

!*******************************************************************************
! Tests various input and output formats
procedure test_formats
  call format "2007:08:06::10:30:14.745" "2007-08-06 10:30:14.745"   "2007:08:06::10:30:14.745" 3 "yyyy-MM-dd hh:mm:ss.SSS"
  call format "2007:08:06::10:30:14.745" "2007:08:06::10:30:14.745"  "2007:08:06::10:30:14.745" 3 "STD"
  call format "2007:08:06::10:30:14.745" "2007:08:06::10:30:14.7"    "2007:08:06::10:30:14.7"   1 "STD"
  call format "2007:08:06::10:30:14.745" "06-AUG-2007:10:30:14.745"  "2007:08:06::10:30:14.745" 3 "VAX"
  ! Since 3.5.2 Bug 2864
  ! 1) test to femtosecond precision
  call format "2007-08-06 10:30:14.745456789012345" "2007-08-06 10:30:14.745456789012345" "2007:08:06::10:30:14.745456789012345" 15 "yyyy-MM-dd hh:mm:ss.SSSSSSSSSSSSSSS"
  ! 2) test case to ensure Time.toDateFormat continues to truncate (rather than round to milliseconds), as rounding
  !    would cause the high precision enhancedSimpleDateFormat values to be incorrect
  call format "2014:01:01::01:02:03.99999" "WED JAN 01 2014 01:02:03.99999" "2014:01:01::01:02:03.99999" 5 "EEE MMM dd yyyy hh:mm:ss.SSSSS"
  call format "2012:12:29::23:59:59.1234" "29 Dec 2012 23:59:59.1234, SAT" "2012:12:29::23:59:59.1234" 4 "dd MMM yyyy HH:mm:ss.SSSS, EEE"
  ! Since 3.5.3 Bug 2876 2883 added support for T:<STD_format_time> as STD Time
  call format "T:2015-3-14::9:26:53.589793238462" "2015:03:14::09:26:53.589793238462" "2015:03:14::09:26:53.589793238462" 12 "STD"
return

procedure format S:input S:output S:parsed L:dp S:format
  timex "^input"  "STR" /fmt="^{format}"   /prec=dp
  assert/text="^reg.main.name: use /FMT='^{format}'"   str eqs output

  timex "^str"    "OUT" /infmt="^{format}" /prec=dp /fmt=STD
  assert/text="^reg.main.name: use /INFMT='^{format}'" out eqs parsed
return

!*******************************************************************************
! Test for Bug 1250 - should show 86400.0 sec as 24:00:00 (midnight of next day)
! and 0.0 sec as 00:00:00 (midnight of current day)
procedure test_sp24
  set t:tests {0={SEC=0,STR="00:00:00.000"},&
               1={SEC=43200,STR="12:00:00.000"},&
               2={SEC=86399,STR="23:59:59.000"},&
               3={SEC=86400,STR="24:00:00.000"},&
               4={SEC=86401,STR="1::00:00:01.000"}}
  foreach test inTable/v tests
    timex/fmt="STD" test.sec t
    assert/text="^reg.main.name: sec=^{test.sec} -> ^{test.str} = ^{t} " test.str eqs t
  endfor
return

!*******************************************************************************
! Tests rounding of output format for Bug 1252
procedure test_rounding
  !               input            expected         dp    format (DEF=HMS)
  call formatTest "00:00:00.001"   "00:00:00.001"    3
  call formatTest "00:00:00.020"   "00:00:00.020"    3
  call formatTest "00:00:00.300"   "00:00:00.300"    3

  ! test round up
  call formatTest "00:00:00.0015"   "00:00:00.002"    3
  call formatTest "00:00:00.0215"   "00:00:00.022"    3
  call formatTest "00:00:00.3215"   "00:00:00.322"    3

  ! test round up to next minute
  call formatTest "00:00:00.9995"   "00:00:01.000"    3
  call formatTest "00:00:01.99951"  "00:00:02.000"    3
  call formatTest "2008:01:10::16:39:29.9995" "16:39:30.000"  3
  set D:t 2008:01:10::16:39:29.9995
  call formatTest "^t" "16:39:30.000"  3 ! <--- this fails under NXM 2.4.2 but above test passes
  call formatTest "16:39:29.99999"  "16:39:30.000"    3
  call formatTest "16:39:29.999999" "16:39:29.999999" 6

  ! test round to next hour
  call formatTest "00:59:59.9999"   "01:00:00.000"    3

  ! test round up pass midnight
  call formatTest "23:59:59.9999"   "24:00:00.000"    3  ! for Bug 1250

  ! test round up to next day
  call formatTest "2008:01:10::23:59:59.99" "2008:01:11" 1 "YMD"

  ! test round up to next month
  call formatTest "2008:01:31::23:59:59.99" "2008:02:01" 1 "YMD"

  ! test round up to next month (leap year)
  call formatTest "2008:02:28::23:59:59.9"  "2008:02:29" 0 "YMD"

  ! test round up to next month (non-leap year)
  call formatTest "2007:02:28::23:59:59.9"  "2007:03:01" 0 "YMD"

  ! test round up to next month (complex non-leap year)
  call formatTest "2100:02:28::23:59:59.9"  "2100:03:01" 0 "YMD"

  ! test round up to next year
  call formatTest "2007:12:31::23:59:59.9"  "2008:01:01" 0 "YMD"

  ! test round down
  call formatTest "00:00:00.0014"   "00:00:00.001"    3
  call formatTest "00:00:00.0021"   "00:00:00.002"    3
  call formatTest "00:00:00.00349"  "00:00:00.003"    3
  call formatTest "00:00:00.0214"   "00:00:00.021"    3
  call formatTest "00:00:00.3214"   "00:00:00.321"    3
  call formatTest "00:00:00.0004"   "00:00:00.000"    3
return

!******************************************************************************!
! test output format with specified input and display precision (dp)
procedure formatTest S:input S:expected L:dp[3] S:format["HMS"]
  timex "^input" "TSTR" /fmt="^{format}" /prec=^dp
  assert/text="^reg.main.name: round got=^tstr expected=^{expected} " tstr eqs expected
return

!******************************************************************************!
procedure test_getTime
  ! Tests the MA.getTime(..) call used by TIMEX
  timex 2008:10:07::12:26:54.112 sec=sec wsec=wsec fsec=fsec
  call do_getTime sec  2008:10:07::12:26:54.112
  call do_getTime sec "2008:10:07::12:26:54.112"
  call do_getTime sec ^sec
  call do_getTime sec (^sec)
  call do_getTime sec (^wsec,^fsec)

  ! Supports legacy X-Midas syntax
  info "This will print two deprecation warnings..."
  call do_getTime sec ^wsec|^fsec
  call do_getTime sec wsec|fsec
return

!******************************************************************************!
procedure do_getTime d:expected u:testCase
  ! Tests the MA.getTime(..) call used by TIMEX
  timex ^testCase sec=actual
  assert/text="^reg.main.name: Testing MA.getTime with ^testCase got ^actual" expected eq actual
return

!*******************************************************************************
! test case for BUG 2321 TIMEX DAY has extra fractional seconds
procedure test_day
  timex DAY currentDay SOD=sod
  assert/text="^reg.main.name: DAY input, ^{currentDay} should have zero secs (i.e SOD==0)" sod eq 0.0
return

!*******************************************************************************
! test case for BUG 3000 TIMEX thinks that 2100 is a leap year
procedure test_leap_year
  timex 2100:02:28::00:00:00.001 d:t_end_feb
  timex 2100:03:01::00:00:00.001 d:t_beg_mar
  calc days_between t_beg_mar t_end_feb - 86400 /
  assert/text="^reg.main.name: days between Feb 28, 2100 & Mar 1, 2010: ^{days_between} (should be 1.0)" days_between eqt 1.0

return

