!******************************************************************************!
!  Purpose: Test the TABLE command
!   Author: J. Czechowski IV / Neon Ngo
!******************************************************************************!
startmacro s:func[""]

  invoke convertExists reg.shell.M.dictionary.isDictionaryEntry(CONVERT)

  if func NEQS "" then
    call test_^{func}
  else
    call test_copy
    call test_create
    call test_equals
    call test_list
    call test_mode         ! tests setmode and getmode
    call test_defaultmode  ! tests setdefaultmode and getdefaultmode
    call test_load
    call test_load_m2k     ! Test loading (a very basic) OPAL table
    call test_load_opal    ! Test loading (a more complex) OPAL table
    call test_save
    call test_getkeys
    call test_getsize
    call test_sort
    call test_table_merge

    call test_ascii
    call test_toString

    call test_getkey_setkey
    call test_foreach_remove

    call test_escapes

    call test_cfg   ! Since 3.1.3 (Bug 2622)

    call test_check_malformed ! since 3.3.0 (Bug 85)

    call test_t_colon_time_in_tables ! since 3.5.3 (Bug 2876 2883)

    call test_table_with_objects_serialization_deserialization ! since 3.5.3 (Bug 2876 2883)

    ! JC4: Not sure where these tests belongs (%test_results?), but for now
    ! I will put them here.
    if convertExists then call test_automatic_convert

    %test_table_whitespace
    %test_table_carets
    %test_table_dot_keys   ! Since NeXtMidas 4.0.1 - NXM-3772
  endif
endmacro

!******************************************************************************!
procedure processException m:emsg
  ! say "Got Exception: emsg=^emsg from=^emsg.from data=^emsg.data"
  if expectException REXISTS then
    assert/text="^reg.main.name: Expected ERROR starts with: ^{expectException}" expectException EQSS emsg.data.message
    remove expectException
  else
    ! Otherwise we should NOT get any exceptions
    assert/text="^reg.main.name: Unexpected ^emsg.data" "FALSE"
  endif
return

!******************************************************************************!
procedure test_copy
  set t:tbl1  { }
  set t:nums {ONE=1,TWO=2}
  set t:days {SUN=1,MON=2,TUE=3,WED=4,THR=5,FRI=6,SAT=7}
  set tbl1.nums nums
  set tbl1.days days


  table tbl1 copy tbl2

  table tbl1 getsize size1
  table tbl2 getsize size2
  assert/text="Size of tbl2 is correct" size1 eq size2

  table tbl1.nums getsize size1
  table tbl2.nums getsize size2
  assert/text="Size of tbl2.nums is correct" size1 eq size2

  table tbl1.days getsize size1
  table tbl2.days getsize size2
  assert/text="Size of tbl2.days is correct" size1 eq size2

  foreach key intable tbl1
    assert/text="tbl2 contains ^{key}" tbl2 contains ^{key}

    foreach subkey intable tbl1.^{key}
      assert/text="tbl2.^{key}.^{subkey} eq tbl1.^{key}.^{subkey}" &
                   tbl2.^{key}.^{subkey} eq tbl1.^{key}.^{subkey}
    endfor
  endfor
return

!******************************************************************************!
procedure test_create
  new java.util.Hashtable ht
  invoke ,, ht.put(A,1)
  invoke ,, ht.put(B,2)
  invoke ,, ht.put(C,3)
                                             ! These match TABLE explain file
  table tab1 create                          !   (1) empty table
  table tab2 create {A=1,B=2,C=3}            !   (2) from string
  table tab3 create ht                       !   (3) from Hashtable
  table tab4 create tab2                     !   (4) from Table
  table tab5 create "nxm.sys.cfg.handlers.tbl" !   (5) from .tbl file
   
  res t:ans {A=1,B=2,C=3}

  assert/text="tab1 exists" tab1 rexists
  assert/text="tab2 exists" tab2 rexists
  assert/text="tab3 exists" tab3 rexists
  assert/text="tab4 exists" tab4 rexists
  assert/text="tab5 exists" tab5 rexists


  ! Test tab2
  foreach key INTABLE ans
    assert/text="tab2.^{key} EQ ^{ans.^{key}}" ^{tab2.^{key}} EQ ^{ans.^{key}}
  endfor

  ! Test tab3
  foreach key INTABLE ans
    assert/text="tab3.^{key} EQ ^{ans.^{key}}" ^{tab3.^{key}} EQ ^{ans.^{key}}
  endfor

  ! Test tab4
  assert/text="tab4 OEQ tab2" tab4 OEQ tab2

  ! Test tab5
  assert/text="Loaded table contains 'file' key" tab5 CONTAINS file
  foreach key INTABLE tab5.file
    assert/text="tab5.file.^{key} EQS ^{reg.handlers.file.^{key}}" ^{tab5.file.^{key}} EQS ^{reg.handlers.file.^{key}}
  endfor

return

!******************************************************************************!
procedure test_equals
  set t:tblA {A=1,B=2,C=3,D=4,E=5} ! These three equal
  set t:tblB {A=1,B=2,C=3,D=4,E=5}
  set t:tblC {B=2,A=1,E=5,C=3,D=4}

  set t:tblZ {A=1,B=2,C=3}

  set t:tbl1 {X=1,Y=2,Z=3.14}         ! these three equal
  set t:tbl2 {X=1.0,Y=2.0,Z=3.14}
  set t:tbl3 {X="1",Y="2",Z="3.14"}

  table tblA equals tblA equal
  assert/text="tblA equals tblA" equal isTrue
  table tblA equals tblB equal
  assert/text="tblA equals tblB" equal isTrue
  table tblA equals tblC equal
  assert/text="tblA equals tblC" equal isTrue
  table tblB equals tblC equal
  assert/text="tblB equals tblC" equal isTrue

  table tbl1 equals tbl1 equal
  assert/text="tbl1 equals tbl1" equal isTrue
  table tbl1 equals tbl2 equal
  assert/text="tbl1 equals tbl2" equal isTrue
  table tbl1 equals tbl3 equal
  assert/text="tbl1 equals tbl3" equal isTrue
  table tbl2 equals tbl3 equal
  assert/text="tbl2 equals tbl3" equal isTrue

  table tblA equals tbl1 equal
  assert/text="tblA equals tbl1" equal isFalse
  table tblA equals tblZ equal
  assert/text="tblA equals tblZ" equal isFalse
return

!******************************************************************************!
procedure test_list
  ! No automated test possible
return

!******************************************************************************!
! test GETFLAGS and SETFLAGS for BUG 2423
procedure test_flags
  res t:tbl1 {}
  table ,, GETDEFAULTFlags defFlags
  table tbl1 getflags flags1
  assert/text="^reg.main.name: Initial table flags matches default table flags" flags1 EQS defFlags

  table tbl1 setflags "+ReadOnly"
  table tbl1 getflags flags2 /verbose
  assert/text="^reg.main.name: After setting flags to +ReadOnly" "ReadOnly" SUBS flags2
return

!******************************************************************************!
procedure test_mode
  res t:tbl1 {}
  table ,, getdefaultmode defMode
  table tbl1 getmode mode
  assert/text="Initial table mode when created from string {} is FB" "^{mode}" EQS "FB"

  res tbl1.one 1
  table tbl1 getmode mode
  assert/text="Default table mode once value added is ^{defMode}" "^{mode}" EQS "^{defMode}"

  table tbl1 setmode "HT"
  table tbl1 getmode mode
  assert/text="After setting mode to HT, mode is HT" "^{mode}" EQS "HT"

  table tbl1 setmode "KV"
  table tbl1 getmode mode
  assert/text="After setting mode to KV, mode is KV" "^{mode}" EQS "KV"

  res t:tbl2 {ONE=1,TWO=2}
  foreach key intable tbl2
    ! do nothing
  endfor
  table tbl2 getmode mode
  assert/text="Default table mode once accessed is ^{defMode}" "^{mode}" EQS "^{defMode}"

return

!******************************************************************************!
procedure test_defaultmode
  table ,, getdefaultmode saveMode

  table ,, setdefaultmode "LHM"
  table ,, getdefaultmode newMode
  res tbl {A=1,B=2}
  res tbl.c 3 ! <-- force table to change from FB to defMode
  table tbl getmode mode
  assert/text="^reg.main.name: Set default mode to LHM"     "LHM" eqs newMode
  assert/text="^reg.main.name: New tables use default mode" "LHM" eqs mode

  table ,, setdefaultmode "TM"
  table ,, getdefaultmode newMode
  res tbl {A=1,B=2}
  res tbl.c 3 ! <-- force table to change from FB to defMode
  table tbl getmode mode
  assert/text="^reg.main.name: Set default mode to TM"      "TM" eqs newMode
  assert/text="^reg.main.name: New tables use default mode" "TM" eqs mode

  table ,, setdefaultmode saveMode
  table ,, getdefaultmode newMode
  assert/text="^reg.main.name: Reset default mode" saveMode eqs newMode
return

!******************************************************************************!
procedure test_load
  
  TABLE tab LOAD "nxm.sys.cfg.handlers.tbl"

  assert/text="Loaded table contains 'file' key" tab CONTAINS file
  foreach key INTABLE tab.file
    assert/text="tab.file.^{key} EQS ^{reg.handlers.file.^{key}}" ^{tab.file.^{key}} EQS ^{reg.handlers.file.^{key}}
  endfor

  !-- test /T modifier with .tbl file extension
  TABLE tab2 LOAD/T nxm.sys.cfg.handlers.tbl
  TABLE tab EQUALS tab2 testResult

  assert/text="LOAD/T with .tbl file extension" testResult isTrue

  !-- test /T modier with .test file extension
  set tempfile temp_test_table_load_t.test
  FILE COPY nxm.sys.cfg.handlers.tbl ^tempfile
  TABLE tab3 LOAD/T ^tempfile
  TABLE tab EQUALS tab3 testResult

  assert/text="LOAD/T with .test file extension" testResult isTrue
  erase/all ^tempfile

  !-- test /X modifer (with .xml file extension)
  TABLE refTable LOAD "nxm.sys.test.test_convert.xml"
  TABLE xtab LOAD/X nxm.sys.test.test_convert.xml
  TABLE refTable EQUALS xtab testResult

  assert/text="LOAD/X with .xml file extension" testResult isTrue

  !-- test /X modifer (with .test file extension)
  set tempxmlfile temp_test_table_load_x.test
  FILE COPY nxm.sys.test.test_convert.xml ^tempxmlfile
  TABLE xtab2 LOAD/X ^tempxmlfile
  TABLE refTable EQUALS xtab2 testResult

  assert/text="LOAD/X with .test file extension" testResult isTrue

  erase/all ^tempxmlfile

  ! test case for Bug 1838 and Bug 1839 to load table file with duplicate keys
  ! they should be loaded into a KeyVector mode Table
  TABLE tab LOAD "nxm.sys.test.plot_setup.tbl"
  TABLE tab GETKEYS/s keys
  ! tests for for Bug 1839 (make sure size is 3 and we have two duplicate keys for FILE=)
  ASSERT/TEXT="LOAD plot_setup.tbl (with duplicate keys) - SIZE" tab.getsize eq 3
  ASSERT/TEXT="LOAD plot_setup.tbl (with duplicate keys) - KEYS" keys eqs "FILE,FILE,VIEW"

  ! tests for Bug 1838 (setting existing key should NOT add to Table)
  res tab.file "test_junk.tmp"
  ASSERT/TEXT="TEST_RESULTS: Set existing key on Table 1st time - SIZE " tab.getsize eq 3
  ASSERT/TEXT="TEST_RESULTS: Set existing key on Table 1st time - VALUE" tab.file    eqs "test_junk.tmp"
  res tab.file "test_junk2.tmp"
  ASSERT/TEXT="TEST_RESULTS: Set existing key on Table 2nd time - SIZE " tab.getsize eq 3
  ASSERT/TEXT="TEST_RESULTS: Set existing key on Table 2nd time - VALUE" tab.file    eqs "test_junk2.tmp"

  ! test case for Bug 2596 for new LOAD/T and /FLAGS= switch to override the
  ! system default flags and NOT use KeyVector mode Table (since 3.1.2)
  TABLE tab LOAD/t nxm.sys.test.plot_setup.tbl /flags=none
  TABLE tab GETKEYS/s keys
  TABLE ,,  GETDEFAULTMODE defmode
  TABLE tab GETMODE        tabmode
  ASSERT/TEXT="LOAD/T with /FLAGS=none plot_setup.tbl (no duplicate keys) - SIZE" tab.getsize eq 2
  ASSERT/TEXT="LOAD/T wtih /FLAGS=none plot_setup.tbl (no duplicate keys) - KEYS" keys eqs "FILE,VIEW"
  ASSERT/TEXT="LOAD/T wtih /FLAGS=none plot_setup.tbl - table mode" tabmode eqs defmode
return

!******************************************************************************!
! Test loading (a very basic) OPAL table - see BUG 965 for more details
procedure test_load_m2k
  res T:m2ktbl "nxm.sys.test.test_table_m2ktbl.tbl"

  res group {TEXT="Basic OPAL table (via RES)"}
  assert/group=group/test m2ktbl.getSize eq 5
  do i 1 5 1
    assert/group=group/test m2ktbl.^{i} eq i
  enddo
  assert/group=group/summary

return

!******************************************************************************!
! Test loading (a more complex) OPAL table - see BUG 965 for more details
procedure test_load_opal
  table m2ktbl LOAD/O   nxm.sys.test.test_table_m2ktbl.tbl
  table opalCS LOAD/O/C nxm.sys.test.test_table_opal.tbl
  table opal   LOAD/O   nxm.sys.test.test_table_opal.tbl

  res group {TEXT="Basic OPAL table (via TABLE)"}
  assert/group=group/test m2ktbl.getSize eq 5
  do i 1 5 1
    assert/group=group/test m2ktbl.^{i} eq i
  enddo
  assert/group=group/summary

  ! Since 3.5.3 Bug 2876 2883 added T:.. Time to file
  timex 2015:01:23::22:57:35.956423789012 tobj !create Time obj for expected value
  set f:expectedF 2.345
  res group {TEXT="Complex OPAL table (case-sensitive)"}
  invoke opalCS_abcd opalCS.abcd  ! <-- trick to access lower-case keys
  invoke opalCS_efgh opalCS.efgh  ! <-- trick to access lower-case keys
  assert/group=group/test opalCS.getSize   eq  14  ! Increased to 14 since 3.7.1 (11 since 3.5.3)
  assert/group=group/test opalCS.TYPE_B    eq  100
  assert/group=group/test opalCS.TYPE_I    eq  12
  assert/group=group/test opalCS.TYPE_F    eq  expectedF
  assert/group=group/test opalCS.1234      eq  3.14
  assert/group=group/test opalCS.ABCD      eq  42.0
  assert/group=group/test opalCS.321       eq  1.23
  assert/group=group/test opalCS_abcd      eq  42.0
  assert/group=group/test opalCS_efgh      eq  42.0
  assert/group=group/test opalCS.TVAL      eq  tobj  ! Since 3.5.3 Bug 2876 2883
  assert/group=group/test opalCS.A         eq  1
  assert/group=group/test opalCS.B         eq  2
  assert/group=group/test opalCS.C         eq  3
  assert/group=group/test opalCS.ZZZ       eqs "String with // in it, but not a comment"
  assert/group=group/test opalCS.SUB.FOO   eq  1.0
  assert/group=group/test opalCS.SUB.BAR   eq  2.0
  assert/group=group/summary

  res group {TEXT="Complex OPAL table (not case-sensitive)"}
  assert/group=group/test opal.getSize   eq  13   ! Increased to 13 since 3.7.1 (10 since 3.5.3)
  assert/group=group/test opal.TYPE_B    eq  100
  assert/group=group/test opal.TYPE_I    eq  12
  assert/group=group/test opal.TYPE_F    eq  expectedF
  assert/group=group/test opal.1234      eq  3.14
  assert/group=group/test opal.ABCD      eq  42.0
  assert/group=group/test opal.321       eq  1.23
  assert/group=group/test opal.EFGH      eq  42.0
  assert/group=group/test opal.TVAL      eq  tobj  ! Since 3.5.3 Bug 2876 2883
  assert/group=group/test opal.A         eq  1
  assert/group=group/test opal.B         eq  2
  assert/group=group/test opal.C         eq  3
  assert/group=group/test opal.ZZZ       eqs "String with // in it, but not a comment"
  assert/group=group/test opal.SUB.FOO   eq  1.0
  assert/group=group/test opal.SUB.BAR   eq  2.0
  assert/group=group/summary
return

!******************************************************************************!
procedure test_save
  set t:mytable {}
  set mytable.NUMBERS {ONE=1,TWO=2,TEN=10}
  set mytable.DAYS    {SUN=1,MON=2,TUE=3,WED=4,THR=5,FRI=6,SAT=7}
  set mytable.ANS     42
  set mytable.COLOR   BLUE
  set mytable.COMMENT "1=Error, 2=Warning, 3=Informational" ! <-- Tests Bug 1430 (string with '=' and ',')

  table mytable save temp_test_table.tbl
  table tab load temp_test_table.tbl

  foreach key INTABLE mytable.numbers
    assert/text="tab.numbers.^{key} EQS ^{mytable.numbers.^{key}}" ^{tab.numbers.^{key}} EQ ^{mytable.numbers.^{key}}
  endfor
  foreach key INTABLE mytable.days
    assert/text="tab.days.^{key} EQS ^{mytable.days.^{key}}" ^{tab.days.^{key}} EQ ^{mytable.days.^{key}}
  endfor
  assert/text="tab.getsize EQ  ^{mytable.getsize}" tab.getsize EQ  mytable.getsize
  assert/text="tab.ans     EQ  ^{mytable.ans}"     tab.ans     EQ  mytable.ans
  assert/text="tab.color   EQS ^{mytable.color}"   tab.color   EQS mytable.color
  assert/text="tab.comment EQS ^{mytable.comment}" tab.comment EQS mytable.comment

  erase/all temp_test_table.tbl
return

!******************************************************************************!
procedure test_getkeys
  set t:nums {ONE=1,TWO=2}
  set t:days {SUN=1,MON=2,TUE=3,WED=4,THR=5,FRI=6,SAT=7}

  table nums getkeys/s keys
  assert/text="KEYS/S in NUMS EQS 'ONE,TWO'" keys eqs "ONE,TWO"
  table days getkeys/s keys
  assert/text="KEYS/S in DAYS EQS 'SUN,MON,TUE,WED,THR,FRI,SAT'" keys eqs "SUN,MON,TUE,WED,THR,FRI,SAT"

  table nums getkeys/s/r keys
  assert/text="KEYS/S/R in NUMS EQS 'TWO,ONE'" keys eqs "TWO,ONE"
  table days getkeys/s/r keys
  assert/text="KEYS/S/R in DAYS EQS 'SAT,FRI,THR,WED,TUE,MON,SUN'" keys eqs "SAT,FRI,THR,WED,TUE,MON,SUN"

  table nums getkeys keys
  invoke keys nxm.sys.lib.StringUtil.join(keys)
  assert/text="KEYS in NUMS EQS 'ONE, TWO'" keys eqs "ONE, TWO"
  table days getkeys keys
  invoke keys nxm.sys.lib.StringUtil.join(keys)
  assert/text="KEYS in DAYS EQS 'SUN, MON, TUE, WED, THR, FRI, SAT'" keys eqs "SUN, MON, TUE, WED, THR, FRI, SAT"

  table nums getkeys/r keys
  invoke keys nxm.sys.lib.StringUtil.join(keys)
  assert/text="KEYS/R in NUMS EQS 'TWO, ONE'" keys eqs "TWO, ONE"
  table days getkeys/r keys
  invoke keys nxm.sys.lib.StringUtil.join(keys)
  assert/text="KEYS/R in DAYS EQS 'SAT, FRI, THR, WED, TUE, MON, SUN'" keys eqs "SAT, FRI, THR, WED, TUE, MON, SUN"
return

!******************************************************************************!
procedure test_getsize
  set t:tbl  { }
  set t:nums {ONE=1,TWO=2}
  set t:days {SUN=1,MON=2,TUE=3,WED=4,THR=5,FRI=6,SAT=7}
  set tbl.nums nums
  set tbl.days days

  table tbl getsize size
  assert/text="Size of tbl is 2" size eq 2

  table nums getsize size
  assert/text="Size of nums is 2" size eq 2

  table days getsize size
  assert/text="Size of days is 7" size eq 7

  ! test case for Bug 1856 (invalid table input should cause an error)
  res expectException "Invalid Table"
  table "test that this cause an error" getsize
  assert/text="Invalid Table given for input parameter should ERROR" expectException NREXISTS

return

!******************************************************************************!
procedure test_sort

  ! SORT BY KEY
  set t:tbl     {ZERO=0,ONE=1,TWO=2,THREE=3,FOUR=4,FIVE=5,SIX=6,SEVEN=7,EIGHT=8,NINE=9}
  set t:forTbl  {ZERO=0,ONE=1,TWO=2,THREE=3,FOUR=4,FIVE=5,SIX=6,SEVEN=7,EIGHT=8,NINE=9}
  set t:revTbl  {ZERO=0,ONE=1,TWO=2,THREE=3,FOUR=4,FIVE=5,SIX=6,SEVEN=7,EIGHT=8,NINE=9}
  set s:forward "EIGHT,FIVE,FOUR,NINE,ONE,SEVEN,SIX,THREE,TWO,ZERO"
  set s:reverse "ZERO,TWO,THREE,SIX,SEVEN,ONE,NINE,FOUR,FIVE,EIGHT"

  table forTbl SORT/K
  table forTbl GETKEYS/S keys
  set ok true
  foreach key intable tbl
    if ok isTrue and tbl.^key eq forTbl.^key then
    endif
  endfor
  assert/text="Correct sort order  with SORT/K   (10 records)" keys eqs forward
  assert/text="Correct sort values with SORT/K   (10 records)" ok isTrue

  table revTbl SORT/K/R
  table revTbl GETKEYS/S keys
  set ok true
  foreach key intable tbl
    if ok isTrue and tbl.^key eq revTbl.^key then
    endif
  endfor
  assert/text="Correct sort order  with SORT/K/R (10 records)" keys eqs reverse
  assert/text="Correct sort values with SORT/K/R (10 records)" ok isTrue


  ! Also check with an odd number of records
  set t:tbl {B=1,C=2,A=3}
  table tbl SORT/K
  table tbl GETKEYS/S keys
  assert/text="Correct sort order  with SORT/K   ( 3 records)" keys eqs "A,B,C"

  set t:tbl {B=1,C=2,A=3}
  table tbl SORT/K/R
  table tbl GETKEYS/S keys
  assert/text="Correct sort order  with SORT/K/R ( 3 records)" keys eqs "C,B,A"

  ! SORT BY VALUE
  ! tbd
return

!******************************************************************************!
procedure  test_table_merge
  ! ==== Test case for merging two or three tables with some duplicate values
  set tbl1 {}
  set tbl1.a 1
  set tbl1.b 2
  set tbl2 {}
  set tbl2.b 20
  set tbl2.c 30
  set tbl2.d 40
  set tbl3 {}
  set tbl3.a 100
  set tbl3.b 200
  set tbl3.c 300

  set tbl4 ^{tbl1}^{tbl2}
  table tbl4 getsize size
  assert/text="Size of tbl4 is 4" size eq 4

  assert/text="Has correct value A=1"  tbl4.a rexists AND tbl4.a eq 1
  assert/text="Has correct value B=20" tbl4.b rexists AND tbl4.b eq 20
  assert/text="Has correct value C=30" tbl4.c rexists AND tbl4.c eq 30
  assert/text="Has correct value D=40" tbl4.d rexists AND tbl4.d eq 40

  set tbl5 ^{tbl1}^{tbl2}^{tbl3}
  table tbl5 getsize size
  assert/text="Size of tbl5 is 4" size eq 4

  assert/text="Has correct value A=100" tbl5.a rexists AND tbl5.a eq 100
  assert/text="Has correct value B=200" tbl5.b rexists AND tbl5.b eq 200
  assert/text="Has correct value C=300" tbl5.c rexists AND tbl5.c eq 300
  assert/text="Has correct value D=40"  tbl5.d rexists AND tbl5.d eq 40

  ! ==== Test case for Bug 1555 (Table: Merging empty table is broken)
  set tbl1 {}
  set tbl1.a 1
  set tbl1.b 2
  set tbl2 {}
  set tbl3 {}
  set tbl3.x 1
  set tbl3.y 2

  set tbl4 ^{tbl1}^{tbl2}^{tbl3}
  table tbl4 getsize size
  assert/text="Size of tbl4 is 4" size eq 4

  assert/text="Has correct value A=1" tbl4.a rexists AND tbl4.a eq 1
  assert/text="Has correct value B=2" tbl4.b rexists AND tbl4.b eq 2
  assert/text="Has correct value X=1" tbl4.x rexists AND tbl4.x eq 1
  assert/text="Has correct value Y=2" tbl4.y rexists AND tbl4.y eq 2

  set tbl5 ^{tbl1}{}^{tbl2}{}^{tbl3}
  table tbl5 getsize size
  assert/text="Size of tbl5 is 4" size eq 4

  assert/text="Has correct value A=1" tbl5.a rexists AND tbl5.a eq 1
  assert/text="Has correct value B=2" tbl5.b rexists AND tbl5.b eq 2
  assert/text="Has correct value X=1" tbl5.x rexists AND tbl5.x eq 1
  assert/text="Has correct value Y=2" tbl5.y rexists AND tbl5.y eq 2

  set tbl6 {}{}^{tbl1}{}^{tbl2}{}^{tbl3}{}{}
  table tbl6 getsize size
  assert/text="Size of tbl6 is 4" size eq 4

  assert/text="Has correct value A=1" tbl6.a rexists AND tbl6.a eq 1
  assert/text="Has correct value B=2" tbl6.b rexists AND tbl6.b eq 2
  assert/text="Has correct value X=1" tbl6.x rexists AND tbl6.x eq 1
  assert/text="Has correct value Y=2" tbl6.y rexists AND tbl6.y eq 2

return

!******************************************************************************!
procedure test_automatic_convert
  ! Converting from a .tbl file
  table refTable create "nxm.sys.cfg.handlers.tbl"        ! <-- This is tested above
  res t:fromFile "nxm.sys.cfg.handlers.tbl"

  TABLE refTable EQUALS fromFile testResult
  assert/text="^reg.main.name: Use of T: syntax with .tbl file" testResult isTrue

  ! Converting from a .xml file
  convert nxm.sys.test.test_convert.xml xml2tab refTable ! <-- Tested in %test_convert
  res t:fromFile "nxm.sys.test.test_convert.xml"

  TABLE refTable EQUALS fromFile testResult
  assert/text="^reg.main.name: Use of T: syntax with .xml file" testResult isTrue
return

!******************************************************************************!
procedure test_ascii
  ! Test with a normal string first
  set titles  {1="Uppercase",2="Case Sensitive",3="Blank",4="Blank, Quoted"}

  set strings {1={SR1=ABC,SR2=The quick brown fix jumps over the lazy dog.},&
               2={SR1="ABC",SR2="The quick brown fix jumps over the lazy dog."},&
               3={SR1=,SR2=},&
               4={SR1="",SR2=""}}
  foreach key intable titles
    res mytable strings.^key
    set table1  ^mytable
    table mytable EQUALS table1 status
    assert/text="Table copy with S: values (^{titles.^{key}})" status isTrue
  endfor


  ! Now test with ASCII strings from a file
  file open myfile nxm.sys.test.test_tablecopy.prm
  do row 0 1 1
    res mytable myfile.getDataTable(^row)
    set table1  ^mytable
    table mytable EQUALS table1 status
    assert/text="Table copy with A: values (row = ^row)" status isTrue
  enddo
  file close myfile
return

!******************************************************************************!
procedure test_toString
  ! Test for Bug 1000 toString() should NOT add extra quotes around a blank table
  new nxm.sys.lib.Table() tab
  set myString "tab=^tab"

  assert/text="^reg.main.name: Table.toString() test with empty table" myString eqs "tab={}"

  ! Test for Bug 1218 Copying tables with members of type "A"
  new nxm.sys.lib.Data("1:1197422519.747852") xxx
  res tbl {}
  res tbl.id xxx
  set tbl2 ^tbl   ! <-- implicit .toString() call
  set str  "^tbl" ! <-- implicit .toString() call

  assert/text="^reg.main.name: Table.toString() with ID='1:1197422519.747852'" tbl2.id eqs "1:1197422519.747852"
return

!******************************************************************************!
! Added for Bug 1791 TABLE REMOVE: Default Table mode (SLHM) incompatible with
! table.remove when iterating or using FOREACH
! This should NOT throw a ConcurrentModificationException.
! Known Issue: this fails when Table Mode is KeyVector (w/o ConcurrentModificationException)
!              To work work KeyVector Table (should iterator over a copy of the Table
!              by careting it, eg. ^tab
!******************************************************************************!
procedure test_foreach_remove
  set t:days {SUN=1,MON=2,TUE=3,WED=4,THR=5,FRI=6,SAT=7}  ! <-- expected results
  set t:tab  {NUMS_1=1,NUMS_2=2,SUN=1,NUMS_3=3,MON=2,TUE=3,WED=4,NUMS_4=4,THR=5,FRI=6,SAT=7,NUMS_5=5,NUMS_6=6}

  foreach key INTABLE tab
    if ^key eqss "NUMS"
      remove tab.^key
    endif
  endfor

  table tab EQUALS days isSame
  assert/text="^reg.main.name: Remove NUMS* keys using FOREACH" isSame isTrue
return

!******************************************************************************!
! test case for and GETKEY and SETKEY function
! Bug 1844 enable getkey for lower-case keys
!******************************************************************************!
procedure test_getkey_setkey
  ! make these results don't exists, since they can mess up test case
  rem key1 upper value value1 value2 value3
  set t:tab {UPPER=one}
  res "TAB.Mixed" 1 ! trick to insert case sensitive key into TAB (practice discouraged)

  ! ==== test GETKEY function ===
  assert/text="^reg.main.name: GETKEY precondition Table size" TAB.GETSIZE EQ 2
  ! get a normal (upper case) key from table
  table tab GETKEY  UPPER value
  assert/text="^reg.main.name: GETKEY on key UPPER - value"      value EQS one
  ! get a mixed case key from table
  table tab GETKEY  "Mixed" value
  assert/text="^reg.main.name: GETKEY on key Mixed - value"      value EQ 1

  ! ==== test SETKEY function ===
  ! key1 gets upper-cased to "KEY1" since it is NOT quoted (default)
  table tab SETKEY  key1  1
  ! quoted "key1" key is put into table as is (lower-case)
  table tab SETKEY "key1"   2
  ! quoted "MixKey" key is put into table as is (mixed-case)
  table tab SETKEY "MixKey" 3
  assert/text="^reg.main.name: SETKEY overall result - size" TAB.GETSIZE EQ 5
  assert/text="^reg.main.name: SETKEY key=KEY1"              TAB.KEY1    EQ 1
  ! more GETKEY test (needed to test that SETKEY worked)
  table tab GETKEY  key1    value1
  table tab GETKEY "key1"   value2
  table tab GETKEY "MixKey" value3
  assert/text="^reg.main.name: SETKEY/GETKEY key=KEY1"   value1 EQ 1
  assert/text="^reg.main.name: SETKEY/GETKEY key=key1"   value2 EQ 2
  assert/text="^reg.main.name: SETKEY/GETKEY key=MixKey" value3 EQ 3

return

!******************************************************************************!
! test case for TABLE getEscapedString function as well as setting
!  the flag disableEscapeSequences.  - Bug 2292
!******************************************************************************!
procedure test_escapes

  TABLE ,, GETDEFAULTFLAGS test_origTblDef
  env set DEFAULTTABLEFLAGS -disableEscapeSequences

  res t:escTbl1 {A="__\t__"}   ! Table with escape sequence
  res s:escStr1 escTbl1.A      ! String form of the table entry value

  ! The length of the String with escapes is longer because it contains the backslash
  invoke str1Length escStr1.length
  invoke withEscapes nxm.sys.lib.StringUtil.escapeString(escStr1)
  invoke str2length withEscapes.length

  assert/text="Escaped Table String Value for Key A: ^escStr1 " str1Length eq 5
  assert/text="Escapes put back in String: ^withEscapes " str2length eq 6

  ! Now get as a String with the escapes via TABLE cmd
  TABLE escTbl1 getEscapedString tblStrWithEsc
  invoke str1Length tblStrWithEsc.length
  assert/text="Table as String with escapes: ^tblStrWithEsc" str1Length eq 12

  ! Do the same thing, but the String should not get translated
  env set DEFAULTTABLEFLAGS +disableEscapeSequences
  res t:escTbl1 {A="__\t__"}   ! Table with escape sequence
  res s:escStr1 escTbl1.A
  invoke str1Length escStr1.length
  invoke withEscapes nxm.sys.lib.StringUtil.escapeString(escStr1)
  invoke str2length withEscapes.length

  assert/text="Escaped Table String (Escapes Disabled): ^escStr1 " str1Length eq 6

  ! reset
  env set DEFAULTTABLEFLAGS ^test_origTblDef

  ! remove results created in this test
  rem escTbl1
  rem escStr1
  rem str1Length
  rem withEscapes
  rem str2length
  rem test_origTblDef

return

!******************************************************************************!
! test case for TABLE LOADCFG and SAVECFG functions - Bug 2622
!******************************************************************************!
procedure test_cfg

  res tmpTable {SECTION1={KEY1="VAL1",KEY2="VAL2"},&
                SECTION2={KEY3="Val3",KEY4="val4"}}
       
  ! add in case-sensitive keys     
  res defTable {}   
  TABLE defTable setKey "key5" "val5"
  TABLE defTable setKey "key4" "val6"
  TABLE tmpTable setKey "DEFAULT" defTable            

  TABLE tmpTable SAVECFG test_table_cfg.cfg
  
  res tmpTable2 {}
  TABLE tmpTable2 LOADCFG test_table_cfg.cfg
  
  ! check Table equality for each section
  foreach key inTab tmpTable
    if key EQS "DEFAULT" then continue    ! DEFAULT is case-sensitive and checked in /C test
    TABLE tmpTable.^key EQUALS tmpTable2.^key rowTest
    assert/text="^reg.main.name: LOAD/SAVE CFG: Section ^key equal: " rowtest isTrue
  endfor
  
  ! merge and check default values
  TABLE tmpTable2 LOADCFG/D test_table_cfg.cfg
  TABLE tmpTable2.SECTION2 EQUALS {key5="val5",KEY4="val4",KEY3="Val3"} rowtest
  assert/text="^reg.main.name: LOADCFG/D merge defaults: " rowtest isTrue
  
  ! check case sensitive values
  TABLE tmpTable2 LOADCFG/C test_table_cfg.cfg
  TABLE tmpTable2.DEFAULT EQUALS defTable rowtest
  assert/text="^reg.main.name: LOADCFG/C case-sensitive keys: " rowtest isTrue
  
  erase test_table_cfg.cfg

return

!******************************************************************************!
! test case for malformed Table checks and warnings - Bug 85
!******************************************************************************!
procedure test_check_malformed
  ! set the checkMalformed flag, but keep track of current setting 
  TABLE ,, GETDEFAULTFLAGS test_origTblDef
  env set DEFAULTTABLEFLAGS +checkMalformed
  
  ! test_table_tab1 and test_table_tab2 should throw an exception and get 'asserted' in processException
  res expectException "Malformed"
  res test_table_tab1 {X=""Y=2}     ! this will cause an exception
  res expectException "Malformed"
  res t:test_table_tab2 {x=5,y=10   ! this will cause an exception
  
  if test_table_tab1 rexists  then
    assert/text="^reg.main.name: Table validation check for String with quotes " 1 eq 2
  elseif test_table_tab2 rexists then
    assert/text="^reg.main.name: Table validation check for String '{x=5,y=10' " 1 eq 2
  endif
  
  res test_table_tab3 {X=1,Y=2,}    ! this is allowed
  assert/text="^reg.main.name: Table with extra comma passes validation" test_table_tab3.x eq 1
  
  ! reset
  env set DEFAULTTABLEFLAGS ^test_origTblDef

return

!******************************************************************************!
! test case for Table with T:<std_format_time> as Time Since 3.5.3 Bug 2876
!******************************************************************************!
procedure test_t_colon_time_in_tables
 res t_colon_tab {K1={KT=T:2012-1-1::20:30:56.123456789012,KD=D:45},K2=2013:03:03::24:56:56.234}
 timex "2012-1-1::20:30:56.123456789012" tobj
 assert/text="^reg.main.name: Table validation check T: Time '{KT=T:2012-1-1::...}' " t_colon_tab.k1.kt eq tobj
 assert/text="^reg.main.name: Table validation check Time String'{K2=2013:03:03::...}' " t_colon_tab.k2 eq 1.993510616234E9

return

!******************************************************************************!
!      Test the serialization and deserialization of TABLEs with objects
!  using different TABLE flags (SerializeTimeAsTColon, DisableEscapeSequences)
!                  Since 3.5.3 - Bug 2876 & Bug 2883
!******************************************************************************!
procedure test_table_with_objects_serialization_deserialization

  !
  ! Create table named: table_with_objects
  ! 
  ! This table holds Time, KeyVector, HashTable, HashMap, TreeMap, LinkedHashMap, 
  ! & ConcurentHashMap Objects
  !
  call create_table_with_objects

  ! Save existing default Table flags, so they can be restored
  table ,, getDefaultFlags defflags

  ! Make sure default uses escape sequences, but does not have serialization
  ! as T:.. time (SerializeTimeAsTColon is false)
  table ,, setDefaultFlags -DisableEscapeSequences|-SerializeTimeAsTColon

  ! Save the table applying the SerializeTimeAsTColon flag just to that table
  res file_with_obj_type_saved test_table_with_objects.tbl
  table table_with_objects save file_with_obj_type_saved /flags=+SerializeTimeAsTColon

  ! Check that tbl file contains expected data
  call check_table_with_objects_saved_with_escapes_tcolon file_with_obj_type_saved

  ! reload the table into a new table (new table should be an exact copy)
  table treload load file_with_obj_type_saved

  ! Check that reLOADed table is equivalent to the original - Objects are preserved
  table table_with_objects equals treload eqres
  assert/text="^reg.main.name: TABLE SAVE/LOAD objects restored" ^eqres ISTRUE

  ! Save the table WITHOUT escape sequence or T:
  ! (+DisableEscapeSequences,-SerializeTimeAsTColon)
  res file_with_obj_type_lost test_table_with_objects_lost.tbl
  table table_with_objects save file_with_obj_type_lost /flags=+DisableEscapeSequences|-SerializeTimeAsTColon
  table treload_lost load file_with_obj_type_lost

  ! Check file to see that Object Types have been lost
  call check_table_with_objects_lost file_with_obj_type_lost

  ! reload the table into a new table (new table NOT HAVE original obj type information)
  table treload_lost load file_with_obj_type_lost

  ! Check that reLOADed table is NOT equivalent to the original - Objects types are lost
  table table_with_objects equals treload_lost eqres
  assert/text="^reg.main.name: TABLE SAVE/LOAD objects not restored" ^eqres ISFALSE

  ! Check StringUtil.toString with Table with objects with different flag settings
  call check_stringutiltostring_table_with_objects

  ! Restore Default Flags
  table ,, setDefaultFlags defflags
  
  ! Erase files
  erase file_with_obj_type_saved
  erase file_with_obj_type_lost

return

!******************************************************************************
! Create a table holding Objects that we support recreation by specifying 
! type during serialization and using that those type characters to 
! deserialize back into the original Object type
!
! Time, KeyVector, HashTable, HashMap, TreeMap, LinkedHashMap, & ConcurentHashMap
!
! (used with test_table__with_objects_serialization_deserialization, since 3.5.3 Bug 2876 & 2883)
!*****************************************************************************
procedure create_table_with_objects

  !
  ! create Time objects to be placed in the Table
  !
  timex T:2015:02:23::23:24:35.5 time_obj1
  timex T:1999-2-1::5:4:6.56789012345 time_obj2
  timex T:1950:1:1::00:00:60 time_obj3

  !
  ! create KeyVector to be placed in the Table
  !
  new nxm.sys.lib.KeyVector kv
  res kv.kt time_obj1 ! cannot use caret, since we need reference, not value (value = double representing sec since 1950)

  !
  ! create HashMap to be placed in the Table
  !
  new java.util.HashMap hm
  invoke ,, hm.put("KEYT",time_obj2)
  invoke ,, hm.put("KEYD",1.23)

  !
  ! create HashTable to be placed in the Table
  ! 
  new java.util.Hashtable ht
  invoke ,, ht.put("KEYL",24) 

  !
  ! create TreeMap to be placed in the Table
  ! 
  new java.util.TreeMap tm
  invoke ,, tm.put("KEYD",82345678123) 

  !
  ! create LinkedHashMap to be placed in the Table
  ! 
  new java.util.LinkedHashMap lhm
  invoke ,, lhm.put("KEYS","Hello") 

  !
  ! create ConcurrentHashMap to be placed in the Table
  ! 
  new java.util.concurrent.ConcurrentHashMap chm
  invoke ,, chm.put("KEYTM",ht) ! cannot use caret, since we need reference, not value

  !
  ! create a Table to hold the KeyVector and HashMap
  !
  res table_with_objects {}
  res table_with_objects.keyk time_obj3 ! cannot use caret, since we need reference, not value
  res table_with_objects.keykv kv
  res table_with_objects.keyhm hm
  res table_with_objects.keyht ht
  res table_with_objects.keytm tm
  res table_with_objects.keylhm lhm
  res table_with_objects.keychm chm

return

!******************************************************************************
! Checks that the table created in procedure create_table_with_objects
! is saved properly when DisableEscapeSequences is false & SerializeTimeAsTColon 
! is true
! (used with test_table__with_objects_serialization_deserialization, since 3.5.3 Bug 2876 & 2883)
!*****************************************************************************
procedure check_table_with_objects_saved_with_escapes_tcolon fn:file_obj_type_saved
  !
  ! Read in each entry as a line
  !
  file open/t tag file_obj_type_saved
  file next tag line_t   ! Time entry
  file next tag line_kv  ! KeyVector entry
  file next tag line_hm  ! HashMap entry
  file next tag line_ht  ! Hashtable entry
  file next tag line_tm  ! TreeMap entry
  file next tag line_lhm ! LinkedHashMap entry
  file next tag line_chm ! ConcurrentHashMap entry
  file close tag

  ! T:.. time when flag SerializeTimeAsTColon is true
  assert/text="^reg.main.name: TABLE SAVE Time object as T:<FULL_STD>" &
              line_t eqs "KEYK=T:1950:01:01::00:01:00.000000000000"
  ! KeyVector saved as O:KV to avoid confusion with X-Midas (K: supported on LOAD)
  ! (when DisableEscapeSequences is false)
  assert/text="^reg.main.name: TABLE SAVE KeyVector object as O:KV" &
              line_kv eqs "KEYKV=O:KV:{KT=T:2015:02:23::23:24:35.500000000000}"
  ! HashMap saved as O:HM when DisableEscapeSequences is false (default)
  assert/text="^reg.main.name: TABLE SAVE HashMap object as O:HM" &
              line_hm eqs "KEYHM=O:HM:{KEYT=T:1999:02:01::05:04:06.567890123450,KEYD=D:1.23}"
  ! HashTable saved as O:HT when DisableEscapeSequences is false (default)
  assert/text="^reg.main.name: TABLE SAVE Hashtable object as O:HT" &
              line_ht eqs "KEYHT=O:HT:{KEYL=L:24}"
  ! TreeMap saved as O:TM when DisableEscapeSequences is false (default)
  assert/text="^reg.main.name: TABLE SAVE TreeMap object as O:TM" &
              line_tm eqs "KEYTM=O:TM:{KEYD=D:8.2345678123E10}"
  ! LinkedHashMap saved as O:LHM when DisableEscapeSequences is false (default)
  res keylhm "KEYLHM=O:LHM:{KEYS=\"Hello\"}"
  assert/text="^reg.main.name: TABLE SAVE LinkedHashMap object as O:LHM" &
              line_lhm eqs keylhm
  ! ConcurrentMap saved as O:CHM when DisableEscapeSequences is false (default)
  assert/text="^reg.main.name: TABLE SAVE ConcurentHashMap object as O:CHM" &
              line_chm eqs "KEYCHM=O:CHM:{KEYTM=O:HT:{KEYL=L:24}}"
 
 return
 
!******************************************************************************
! Checks that the table created in procedure create_table_with_objects
! is saved properly when DisableEscapeSequences is true & SerializeTimeAsTColon 
! is false - Object types will be lost in the SAVE
! ! (used with test_table__with_objects_serialization_deserialization, since 3.5.3 Bug 2876 & 2883)
!*****************************************************************************
procedure check_table_with_objects_lost fn:file_obj_type_lost
  !
  ! Read in each entry as a line
  !
  file open/t tag file_obj_type_lost
  file next tag line_t   ! Time entry
  file next tag line_kv  ! KeyVector entry
  file next tag line_hm  ! HashMap entry
  file next tag line_ht  ! Hashtable entry
  file next tag line_tm  ! TreeMap entry
  file next tag line_lhm ! LinkedHashMap entry
  file next tag line_chm ! ConcurrentHashMap entry
  file close tag

  ! Time obj saved as plain Time String when SerializeTimeAsTColon is false
  res keyk "KEYK=\"00:01:00.000\""
  assert/text="^reg.main.name: TABLE SAVE Time object as Time string" &
              line_t starts keyk
  ! KeyVector saved as Table when DisableEscapeSequences is true
  res keykv "KEYKV={KT=\"2015:02:23::23:24:35.500\"}"
  assert/text="^reg.main.name: TABLE SAVE KeyVector object as Table" &
              line_kv eqs keykv
  ! HashMap saved as Table DisableEscapeSequences is true
  res keyhm "KEYHM={KEYT="1999:02:01::05:04:06.568",KEYD=D:1.23}"
  assert/text="^reg.main.name: TABLE SAVE HashMap object as Table" &
              line_hm eqs keyhm
  ! HashTable saved as Table when DisableEscapeSequences is true
  assert/text="^reg.main.name: TABLE SAVE Hashtable object as Table" &
              line_ht eqs "KEYHT={KEYL=L:24}"
  ! TreeMap saved as Table when DisableEscapeSequences is true
  assert/text="^reg.main.name: TABLE SAVE TreeMap object as Table" &
              line_tm eqs "KEYTM={KEYD=D:8.2345678123E10}"
  ! LinkedHashMap saved as Table when DisableEscapeSequences is true
  res keylhm "KEYLHM={KEYS=\"Hello\"}"
  assert/text="^reg.main.name: TABLE SAVE LinkedHashMap object as Table" &
              line_lhm eqs keylhm
  ! ConcurrentMap saved asTable when DisableEscapeSequences is true
  assert/text="^reg.main.name: TABLE SAVE ConcurentHashMap object as Table" &
              line_chm eqs "KEYCHM={KEYTM={KEYL=L:24}}"
 
 return
 
!****************************************************************************
! Check StringUtil.toString with Table with objects with different flag settings
! (used with test_table__with_objects_serialization_deserialization, since 3.5.3 Bug 2876 & 2883)
!****************************************************************************
procedure check_stringutiltostring_table_with_objects

  ! Save Flags entering this procedure, so they can be restored
  table ,, getDefaultFlags defflags_p
  table table_with_objects getFlags tableflags_p

  ! Check StringUtil.toString with:
  !  Default Table flags: SerializeTimeAsTColon off (false)
  !  Individual Table flag that will be IGNORED: SerializeTimeAsTColon off (false)
  !  DisableEscapeSequences off on both - will be IGNORED
  table ,, setDefaultFlags -DisableEscapeSequences|-SerializeTimeAsTColon
  table table_with_objects setFlags -DisableEscapeSequences|+SerializeTimeAsTColon
  invoke res_string_util_off nxm.sys.lib.StringUtil.toString(table_with_objects)
  res exp_pt1 "{KEYK=\"00:01:00.000\",KEYKV={KT=\"2015:02:23::23:24:35.500\"},"
  res exp_pt2 "KEYHM={KEYT=\"1999:02:01::05:04:06.568\",KEYD=D:1.23},KEYHT={KEYL=L:24},"
  res exp_pt3 "KEYTM={KEYD=D:8.2345678123E10},KEYLHM={KEYS=\"Hello\"},KEYCHM={KEYTM={KEYL=L:24}}}"
  invoke pt1pt2 exp_pt1.concat(exp_pt2) 
  invoke expected pt1pt2.concat(exp_pt3)
  assert/text="^reg.main.name: StringUtil.toString(table_with_Time) Time as STD String" res_string_util_off eqs expected

  ! Check StringUtil.toString with:
  !  Default Table flags: SerializeTimeAsTColon off (false)
  !  Individual Table flag that will be IGNORED: SerializeTimeAsTColon off (false)
  !  DisableEscapeSequences on on both - will be IGNORED (tests masking logic)
  table ,, setDefaultFlags +DisableEscapeSequences|+SerializeTimeAsTColon
  table table_with_objects setFlags +DisableEscapeSequences|-SerializeTimeAsTColon
  invoke res_string_util_on nxm.sys.lib.StringUtil.toString(table_with_objects)
  res exp_pt1 "{KEYK=T:1950:01:01::00:01:00.000000000000,KEYKV={KT=T:2015:02:23::23:24:35.500000000000},"
  res exp_pt2 "KEYHM={KEYT=T:1999:02:01::05:04:06.567890123450,KEYD=D:1.23},KEYHT={KEYL=L:24},"
  res exp_pt3 "KEYTM={KEYD=D:8.2345678123E10},KEYLHM={KEYS=\"Hello\"},KEYCHM={KEYTM={KEYL=L:24}}}"
  invoke pt1pt2 exp_pt1.concat(exp_pt2) 
  invoke expected pt1pt2.concat(exp_pt3)
  assert/text="^reg.main.name: StringUtil.toString(table_with_Time) Time as T:<FULL_STD>" res_string_util_on eqs expected

  ! Restore Default Flags to what they were entering this procedure
  table ,, setDefaultFlags defflags_p
  table table_with_objects setFlags tableflags_p

 return
